1d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick/* 2d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * Copyright (C) 2010 The Android Open Source Project 3d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * 4d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * Licensed under the Apache License, Version 2.0 (the "License"); 5d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * you may not use this file except in compliance with the License. 6d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * You may obtain a copy of the License at 7d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * 8d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * http://www.apache.org/licenses/LICENSE-2.0 9d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * 10d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * Unless required by applicable law or agreed to in writing, software 11d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * distributed under the License is distributed on an "AS IS" BASIS, 12d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * See the License for the specific language governing permissions and 14d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * limitations under the License. 15d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick */ 16d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 17d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickpackage android.app; 18d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 19d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport android.content.SharedPreferences; 20d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport android.os.FileUtils; 21d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport android.os.Looper; 22d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport android.util.Log; 23d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 24d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport com.google.android.collect.Maps; 25d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport com.android.internal.util.XmlUtils; 26d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 274cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrickimport dalvik.system.BlockGuard; 284cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick 29d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport org.xmlpull.v1.XmlPullParserException; 30d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 312e8fb73ec088dbe459b07475aee1e43b500f101cDianne Hackbornimport java.io.BufferedInputStream; 32d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.io.File; 334cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrickimport java.io.FileInputStream; 34d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.io.FileNotFoundException; 35d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.io.FileOutputStream; 36d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.io.IOException; 37d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.ArrayList; 38d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.HashMap; 39d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.HashSet; 40d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.List; 41d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.Map; 42d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.Set; 43d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.WeakHashMap; 44d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.concurrent.CountDownLatch; 45d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.concurrent.ExecutorService; 46d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 4798e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Rootimport libcore.io.ErrnoException; 4898e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Rootimport libcore.io.IoUtils; 4998e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Rootimport libcore.io.Libcore; 5098e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Rootimport libcore.io.StructStat; 5198e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root 52d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickfinal class SharedPreferencesImpl implements SharedPreferences { 53d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private static final String TAG = "SharedPreferencesImpl"; 54d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private static final boolean DEBUG = false; 55d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 56d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Lock ordering rules: 57d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // - acquire SharedPreferencesImpl.this before EditorImpl.this 58d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // - acquire mWritingToDiskLock before EditorImpl.this 59d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 60d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private final File mFile; 61d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private final File mBackupFile; 62d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private final int mMode; 63d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 64d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private Map<String, Object> mMap; // guarded by 'this' 65d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private int mDiskWritesInFlight = 0; // guarded by 'this' 66d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private boolean mLoaded = false; // guarded by 'this' 67d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private long mStatTimestamp; // guarded by 'this' 68d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private long mStatSize; // guarded by 'this' 69d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 70d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private final Object mWritingToDiskLock = new Object(); 71d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private static final Object mContent = new Object(); 724cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners = 734cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick new WeakHashMap<OnSharedPreferenceChangeListener, Object>(); 74d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 754cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick SharedPreferencesImpl(File file, int mode) { 76d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mFile = file; 774cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick mBackupFile = makeBackupFile(file); 78d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mMode = mode; 794cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick mLoaded = false; 804cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick mMap = null; 814cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick startLoadFromDisk(); 824cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 834cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick 844cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick private void startLoadFromDisk() { 854cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick synchronized (this) { 864cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick mLoaded = false; 874cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 884cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick new Thread("SharedPreferencesImpl-load") { 894cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick public void run() { 904cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick synchronized (SharedPreferencesImpl.this) { 914cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick loadFromDiskLocked(); 924cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 934cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 944cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick }.start(); 954cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 964cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick 974cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick private void loadFromDiskLocked() { 984cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick if (mLoaded) { 994cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick return; 1004cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 1014cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick if (mBackupFile.exists()) { 1024cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick mFile.delete(); 1034cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick mBackupFile.renameTo(mFile); 1044cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 1054cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick 1064cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // Debugging 1074cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick if (mFile.exists() && !mFile.canRead()) { 1084cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission"); 1094cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 1104cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick 1114cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick Map map = null; 11298e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root StructStat stat = null; 11398e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root try { 11498e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root stat = Libcore.os.stat(mFile.getPath()); 11598e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root if (mFile.canRead()) { 11698e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root BufferedInputStream str = null; 11798e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root try { 11898e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root str = new BufferedInputStream( 11998e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root new FileInputStream(mFile), 16*1024); 12098e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root map = XmlUtils.readMapXml(str); 12198e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root } catch (XmlPullParserException e) { 12298e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root Log.w(TAG, "getSharedPreferences", e); 12398e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root } catch (FileNotFoundException e) { 12498e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root Log.w(TAG, "getSharedPreferences", e); 12598e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root } catch (IOException e) { 12698e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root Log.w(TAG, "getSharedPreferences", e); 12798e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root } finally { 12898e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root IoUtils.closeQuietly(str); 12998e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root } 1304cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 13198e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root } catch (ErrnoException e) { 1324cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 1334cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick mLoaded = true; 1344cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick if (map != null) { 1354cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick mMap = map; 13698e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root mStatTimestamp = stat.st_mtime; 13798e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root mStatSize = stat.st_size; 1384cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } else { 1394cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick mMap = new HashMap<String, Object>(); 140d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 1414cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick notifyAll(); 1424cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 1434cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick 1444cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick private static File makeBackupFile(File prefsFile) { 1454cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick return new File(prefsFile.getPath() + ".bak"); 146d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 147d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 1484cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick void startReloadIfChangedUnexpectedly() { 149d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 1504cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // TODO: wait for any pending writes to disk? 1514cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick if (!hasFileChangedUnexpectedly()) { 1524cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick return; 1534cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 1544cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick startLoadFromDisk(); 155d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 156d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 157d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 158d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Has the file changed out from under us? i.e. writes that 159d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // we didn't instigate. 1604cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick private boolean hasFileChangedUnexpectedly() { 161d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 162d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (mDiskWritesInFlight > 0) { 163d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // If we know we caused it, it's not unexpected. 164d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (DEBUG) Log.d(TAG, "disk write in flight, not unexpected."); 165d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return false; 166d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 167d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 16898e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root 16998e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root final StructStat stat; 17098e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root try { 17198e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root /* 17298e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root * Metadata operations don't usually count as a block guard 17398e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root * violation, but we explicitly want this one. 17498e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root */ 17598e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root BlockGuard.getThreadPolicy().onReadFromDisk(); 17698e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root stat = Libcore.os.stat(mFile.getPath()); 17798e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root } catch (ErrnoException e) { 178d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return true; 179d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 18098e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root 181d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 18298e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root return mStatTimestamp != stat.st_mtime || mStatSize != stat.st_size; 183d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 184d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 185d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 186d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { 187d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized(this) { 188d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mListeners.put(listener, mContent); 189d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 190d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 191d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 192d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { 193d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized(this) { 194d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mListeners.remove(listener); 195d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 196d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 197d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 1984cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick private void awaitLoadedLocked() { 1994cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick if (!mLoaded) { 2004cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // Raise an explicit StrictMode onReadFromDisk for this 2014cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // thread, since the real read will be in a different 2024cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // thread and otherwise ignored by StrictMode. 2034cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick BlockGuard.getThreadPolicy().onReadFromDisk(); 2044cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 2054cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick while (!mLoaded) { 2064cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick try { 2074cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick wait(); 2084cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } catch (InterruptedException unused) { 2094cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 2104cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 2114cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 2124cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick 213d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Map<String, ?> getAll() { 2144cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick synchronized (this) { 2154cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 216d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick //noinspection unchecked 217d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return new HashMap<String, Object>(mMap); 218d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 219d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 220d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 221d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public String getString(String key, String defValue) { 222d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2234cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 224d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick String v = (String)mMap.get(key); 225d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return v != null ? v : defValue; 226d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 227d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 228d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 229d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Set<String> getStringSet(String key, Set<String> defValues) { 230d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2314cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 232d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Set<String> v = (Set<String>) mMap.get(key); 233d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return v != null ? v : defValues; 234d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 235d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 236d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 237d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public int getInt(String key, int defValue) { 238d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2394cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 240d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Integer v = (Integer)mMap.get(key); 241d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return v != null ? v : defValue; 242d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 243d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 244d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public long getLong(String key, long defValue) { 245d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2464cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 247d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Long v = (Long)mMap.get(key); 248d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return v != null ? v : defValue; 249d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 250d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 251d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public float getFloat(String key, float defValue) { 252d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2534cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 254d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Float v = (Float)mMap.get(key); 255d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return v != null ? v : defValue; 256d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 257d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 258d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public boolean getBoolean(String key, boolean defValue) { 259d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2604cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 261d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Boolean v = (Boolean)mMap.get(key); 262d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return v != null ? v : defValue; 263d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 264d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 265d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 266d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public boolean contains(String key) { 267d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2684cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 269d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return mMap.containsKey(key); 270d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 271d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 272d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 273d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor edit() { 2744cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // TODO: remove the need to call awaitLoadedLocked() when 2754cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // requesting an editor. will require some work on the 2764cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // Editor, but then we should be able to do: 2774cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // 2784cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // context.getSharedPreferences(..).edit().putString(..).apply() 2794cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // 2804cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // ... all without blocking. 2814cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick synchronized (this) { 2824cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 2834cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 2844cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick 285d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return new EditorImpl(); 286d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 287d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 288d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Return value from EditorImpl#commitToMemory() 289d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private static class MemoryCommitResult { 290d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public boolean changesMade; // any keys different? 291d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public List<String> keysModified; // may be null 292d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Set<OnSharedPreferenceChangeListener> listeners; // may be null 293d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Map<?, ?> mapToWriteToDisk; 294d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public final CountDownLatch writtenToDiskLatch = new CountDownLatch(1); 295d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public volatile boolean writeToDiskResult = false; 296d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 297d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void setDiskWriteResult(boolean result) { 298d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick writeToDiskResult = result; 299d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick writtenToDiskLatch.countDown(); 300d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 301d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 302d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 303d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public final class EditorImpl implements Editor { 304d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private final Map<String, Object> mModified = Maps.newHashMap(); 305d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private boolean mClear = false; 306d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 307d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor putString(String key, String value) { 308d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 309d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.put(key, value); 310d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 311d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 312d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 313d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor putStringSet(String key, Set<String> values) { 314d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 31501ed79c5786c527628544828abf8b70d02b989cdChristopher Tate mModified.put(key, 31601ed79c5786c527628544828abf8b70d02b989cdChristopher Tate (values == null) ? null : new HashSet<String>(values)); 317d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 318d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 319d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 320d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor putInt(String key, int value) { 321d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 322d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.put(key, value); 323d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 324d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 325d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 326d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor putLong(String key, long value) { 327d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 328d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.put(key, value); 329d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 330d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 331d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 332d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor putFloat(String key, float value) { 333d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 334d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.put(key, value); 335d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 336d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 337d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 338d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor putBoolean(String key, boolean value) { 339d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 340d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.put(key, value); 341d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 342d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 343d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 344d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 345d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor remove(String key) { 346d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 347d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.put(key, this); 348d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 349d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 350d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 351d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 352d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor clear() { 353d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 354d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mClear = true; 355d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 356d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 357d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 358d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 359d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void apply() { 360d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick final MemoryCommitResult mcr = commitToMemory(); 361d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick final Runnable awaitCommit = new Runnable() { 362d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void run() { 363d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick try { 364d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.writtenToDiskLatch.await(); 365d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } catch (InterruptedException ignored) { 366d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 367d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 368d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick }; 369d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 370d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick QueuedWork.add(awaitCommit); 371d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 372d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Runnable postWriteRunnable = new Runnable() { 373d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void run() { 374d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick awaitCommit.run(); 375d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick QueuedWork.remove(awaitCommit); 376d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 377d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick }; 378d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 379d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable); 380d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 381d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Okay to notify the listeners before it's hit disk 382d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // because the listeners should always get the same 383d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // SharedPreferences instance back, which has the 384d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // changes reflected in memory. 385d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick notifyListeners(mcr); 386d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 387d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 388d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Returns true if any changes were made 389d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private MemoryCommitResult commitToMemory() { 390d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick MemoryCommitResult mcr = new MemoryCommitResult(); 391d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (SharedPreferencesImpl.this) { 392d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // We optimistically don't make a deep copy until 393d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // a memory commit comes in when we're already 394d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // writing to disk. 395d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (mDiskWritesInFlight > 0) { 396d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // We can't modify our mMap as a currently 397d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // in-flight write owns it. Clone it before 398d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // modifying it. 399d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // noinspection unchecked 400d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mMap = new HashMap<String, Object>(mMap); 401d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 402d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.mapToWriteToDisk = mMap; 403d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mDiskWritesInFlight++; 404d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 405d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick boolean hasListeners = mListeners.size() > 0; 406d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (hasListeners) { 407d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.keysModified = new ArrayList<String>(); 408d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.listeners = 409d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet()); 410d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 411d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 412d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 413d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (mClear) { 414d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!mMap.isEmpty()) { 415d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.changesMade = true; 416d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mMap.clear(); 417d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 418d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mClear = false; 419d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 420d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 421d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick for (Map.Entry<String, Object> e : mModified.entrySet()) { 422d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick String k = e.getKey(); 423d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Object v = e.getValue(); 424d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (v == this) { // magic value for a removal mutation 425d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!mMap.containsKey(k)) { 426d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick continue; 427d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 428d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mMap.remove(k); 429d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } else { 430d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick boolean isSame = false; 431d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (mMap.containsKey(k)) { 432d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Object existingValue = mMap.get(k); 433d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (existingValue != null && existingValue.equals(v)) { 434d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick continue; 435d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 436d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 437d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mMap.put(k, v); 438d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 439d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 440d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.changesMade = true; 441d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (hasListeners) { 442d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.keysModified.add(k); 443d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 444d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 445d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 446d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.clear(); 447d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 448d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 449d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return mcr; 450d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 451d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 452d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public boolean commit() { 453d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick MemoryCommitResult mcr = commitToMemory(); 454d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick SharedPreferencesImpl.this.enqueueDiskWrite( 455d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr, null /* sync write on this thread okay */); 456d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick try { 457d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.writtenToDiskLatch.await(); 458d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } catch (InterruptedException e) { 459d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return false; 460d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 461d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick notifyListeners(mcr); 462d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return mcr.writeToDiskResult; 463d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 464d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 465d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private void notifyListeners(final MemoryCommitResult mcr) { 466d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (mcr.listeners == null || mcr.keysModified == null || 467d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.keysModified.size() == 0) { 468d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return; 469d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 470d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (Looper.myLooper() == Looper.getMainLooper()) { 471d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick for (int i = mcr.keysModified.size() - 1; i >= 0; i--) { 472d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick final String key = mcr.keysModified.get(i); 473d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick for (OnSharedPreferenceChangeListener listener : mcr.listeners) { 474d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (listener != null) { 475d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick listener.onSharedPreferenceChanged(SharedPreferencesImpl.this, key); 476d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 477d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 478d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 479d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } else { 480d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Run this function on the main thread. 481d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick ActivityThread.sMainThreadHandler.post(new Runnable() { 482d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void run() { 483d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick notifyListeners(mcr); 484d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 485d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick }); 486d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 487d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 488d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 489d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 490d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick /** 491d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * Enqueue an already-committed-to-memory result to be written 492d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * to disk. 493d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * 494d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * They will be written to disk one-at-a-time in the order 495d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * that they're enqueued. 496d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * 497d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * @param postWriteRunnable if non-null, we're being called 498d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * from apply() and this is the runnable to run after 499d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * the write proceeds. if null (from a regular commit()), 500d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * then we're allowed to do this disk write on the main 501d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * thread (which in addition to reducing allocations and 502d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * creating a background thread, this has the advantage that 503d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * we catch them in userdebug StrictMode reports to convert 504d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * them where possible to apply() ...) 505d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick */ 506d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private void enqueueDiskWrite(final MemoryCommitResult mcr, 507d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick final Runnable postWriteRunnable) { 508d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick final Runnable writeToDiskRunnable = new Runnable() { 509d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void run() { 510d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (mWritingToDiskLock) { 511d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick writeToFile(mcr); 512d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 513d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (SharedPreferencesImpl.this) { 514d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mDiskWritesInFlight--; 515d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 516d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (postWriteRunnable != null) { 517d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick postWriteRunnable.run(); 518d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 519d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 520d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick }; 521d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 522d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick final boolean isFromSyncCommit = (postWriteRunnable == null); 523d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 524d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Typical #commit() path with fewer allocations, doing a write on 525d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // the current thread. 526d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (isFromSyncCommit) { 527d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick boolean wasEmpty = false; 528d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (SharedPreferencesImpl.this) { 529d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick wasEmpty = mDiskWritesInFlight == 1; 530d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 531d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (wasEmpty) { 532d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick writeToDiskRunnable.run(); 533d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return; 534d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 535d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 536d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 537d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable); 538d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 539d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 540d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private static FileOutputStream createFileOutputStream(File file) { 541d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick FileOutputStream str = null; 542d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick try { 543d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick str = new FileOutputStream(file); 544d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } catch (FileNotFoundException e) { 545d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick File parent = file.getParentFile(); 546d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!parent.mkdir()) { 547d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Log.e(TAG, "Couldn't create directory for SharedPreferences file " + file); 548d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return null; 549d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 550d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick FileUtils.setPermissions( 551d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick parent.getPath(), 552d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, 553d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick -1, -1); 554d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick try { 555d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick str = new FileOutputStream(file); 556d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } catch (FileNotFoundException e2) { 557d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Log.e(TAG, "Couldn't create SharedPreferences file " + file, e2); 558d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 559d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 560d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return str; 561d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 562d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 563d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Note: must hold mWritingToDiskLock 564d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private void writeToFile(MemoryCommitResult mcr) { 565d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Rename the current file so it may be used as a backup during the next read 566d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (mFile.exists()) { 567d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!mcr.changesMade) { 568d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // If the file already exists, but no changes were 569d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // made to the underlying map, it's wasteful to 570d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // re-write the file. Return as if we wrote it 571d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // out. 572d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.setDiskWriteResult(true); 573d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return; 574d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 575d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!mBackupFile.exists()) { 576d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!mFile.renameTo(mBackupFile)) { 577d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Log.e(TAG, "Couldn't rename file " + mFile 578d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick + " to backup file " + mBackupFile); 579d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.setDiskWriteResult(false); 580d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return; 581d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 582d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } else { 583d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mFile.delete(); 584d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 585d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 586d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 587d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Attempt to write the file, delete the backup and return true as atomically as 588d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // possible. If any exception occurs, delete the new file; next time we will restore 589d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // from the backup. 590d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick try { 591d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick FileOutputStream str = createFileOutputStream(mFile); 592d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (str == null) { 593d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.setDiskWriteResult(false); 594d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return; 595d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 596d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str); 597d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick FileUtils.sync(str); 598d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick str.close(); 599d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0); 60098e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root try { 60198e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root final StructStat stat = Libcore.os.stat(mFile.getPath()); 602d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 60398e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root mStatTimestamp = stat.st_mtime; 60498e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root mStatSize = stat.st_size; 605d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 60698e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root } catch (ErrnoException e) { 60798e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root // Do nothing 608d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 609d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Writing was successful, delete the backup file if there is one. 610d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mBackupFile.delete(); 611d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.setDiskWriteResult(true); 612d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return; 613d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } catch (XmlPullParserException e) { 614d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Log.w(TAG, "writeToFile: Got exception:", e); 615d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } catch (IOException e) { 616d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Log.w(TAG, "writeToFile: Got exception:", e); 617d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 618d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Clean up an unsuccessfully written file 619d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (mFile.exists()) { 620d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!mFile.delete()) { 621d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Log.e(TAG, "Couldn't clean up partially-written file " + mFile); 622d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 623d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 624d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.setDiskWriteResult(false); 625d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 626d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick} 627