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 192333e3406c1742d0755b4a80daaf717aa50cd1e5Scott Kennedyimport android.annotation.Nullable; 20d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport android.content.SharedPreferences; 21d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport android.os.FileUtils; 22d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport android.os.Looper; 2334385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.ErrnoException; 2434385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.Os; 2534385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.StructStat; 26d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport android.util.Log; 27d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 28d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport com.google.android.collect.Maps; 29d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport com.android.internal.util.XmlUtils; 30d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 314cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrickimport dalvik.system.BlockGuard; 324cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick 33d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport org.xmlpull.v1.XmlPullParserException; 34d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 352e8fb73ec088dbe459b07475aee1e43b500f101cDianne Hackbornimport java.io.BufferedInputStream; 36d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.io.File; 374cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrickimport java.io.FileInputStream; 38d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.io.FileNotFoundException; 39d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.io.FileOutputStream; 40d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.io.IOException; 41d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.ArrayList; 42d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.HashMap; 43d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.HashSet; 44d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.List; 45d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.Map; 46d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.Set; 47d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.WeakHashMap; 48d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrickimport java.util.concurrent.CountDownLatch; 49d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 5098e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Rootimport libcore.io.IoUtils; 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() { 9096db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov loadFromDisk(); 914cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 924cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick }.start(); 934cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 944cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick 9596db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov private void loadFromDisk() { 9696db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov synchronized (SharedPreferencesImpl.this) { 9796db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov if (mLoaded) { 9896db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov return; 9996db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov } 10096db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov if (mBackupFile.exists()) { 10196db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov mFile.delete(); 10296db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov mBackupFile.renameTo(mFile); 10396db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov } 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 { 11434385d352da19805ae948215e2edbeedd16b7941Elliott Hughes stat = 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); 12196db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov } catch (XmlPullParserException | IOException e) { 12298e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root Log.w(TAG, "getSharedPreferences", e); 12398e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root } finally { 12498e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root IoUtils.closeQuietly(str); 12598e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root } 1264cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 12798e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root } catch (ErrnoException e) { 12896db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov /* ignore */ 1294cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 13096db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov 13196db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov synchronized (SharedPreferencesImpl.this) { 13296db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov mLoaded = true; 13396db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov if (map != null) { 13496db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov mMap = map; 13596db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov mStatTimestamp = stat.st_mtime; 13696db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov mStatSize = stat.st_size; 13796db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov } else { 13896db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov mMap = new HashMap<>(); 13996db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov } 14096db26032e87c0b5c0f150c1a8541baaad2ea9bbSvet Ganov notifyAll(); 141d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 1424cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 1434cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick 14435871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey 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(); 17634385d352da19805ae948215e2edbeedd16b7941Elliott Hughes stat = 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 2212333e3406c1742d0755b4a80daaf717aa50cd1e5Scott Kennedy @Nullable 2222333e3406c1742d0755b4a80daaf717aa50cd1e5Scott Kennedy public String getString(String key, @Nullable String defValue) { 223d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2244cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 225d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick String v = (String)mMap.get(key); 226d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return v != null ? v : defValue; 227d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 228d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 229d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 2302333e3406c1742d0755b4a80daaf717aa50cd1e5Scott Kennedy @Nullable 2312333e3406c1742d0755b4a80daaf717aa50cd1e5Scott Kennedy public Set<String> getStringSet(String key, @Nullable Set<String> defValues) { 232d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2334cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 234d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Set<String> v = (Set<String>) mMap.get(key); 235d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return v != null ? v : defValues; 236d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 237d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 238d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 239d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public int getInt(String key, int defValue) { 240d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2414cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 242d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Integer v = (Integer)mMap.get(key); 243d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return v != null ? v : defValue; 244d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 245d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 246d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public long getLong(String key, long defValue) { 247d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2484cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 249d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Long v = (Long)mMap.get(key); 250d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return v != null ? v : defValue; 251d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 252d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 253d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public float getFloat(String key, float defValue) { 254d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2554cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 256d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Float v = (Float)mMap.get(key); 257d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return v != null ? v : defValue; 258d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 259d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 260d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public boolean getBoolean(String key, boolean defValue) { 261d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2624cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 263d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Boolean v = (Boolean)mMap.get(key); 264d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return v != null ? v : defValue; 265d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 266d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 267d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 268d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public boolean contains(String key) { 269d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 2704cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 271d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return mMap.containsKey(key); 272d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 273d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 274d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 275d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor edit() { 2764cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // TODO: remove the need to call awaitLoadedLocked() when 2774cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // requesting an editor. will require some work on the 2784cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // Editor, but then we should be able to do: 2794cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // 2804cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // context.getSharedPreferences(..).edit().putString(..).apply() 2814cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // 2824cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick // ... all without blocking. 2834cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick synchronized (this) { 2844cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick awaitLoadedLocked(); 2854cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick } 2864cd50b8d76e08d75df1a2e9d4902bcdc880bd1c1Brad Fitzpatrick 287d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return new EditorImpl(); 288d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 289d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 290d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Return value from EditorImpl#commitToMemory() 291d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private static class MemoryCommitResult { 292d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public boolean changesMade; // any keys different? 293d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public List<String> keysModified; // may be null 294d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Set<OnSharedPreferenceChangeListener> listeners; // may be null 295d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Map<?, ?> mapToWriteToDisk; 296d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public final CountDownLatch writtenToDiskLatch = new CountDownLatch(1); 297d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public volatile boolean writeToDiskResult = false; 298d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 299d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void setDiskWriteResult(boolean result) { 300d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick writeToDiskResult = result; 301d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick writtenToDiskLatch.countDown(); 302d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 303d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 304d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 305d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public final class EditorImpl implements Editor { 306d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private final Map<String, Object> mModified = Maps.newHashMap(); 307d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private boolean mClear = false; 308d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 3092333e3406c1742d0755b4a80daaf717aa50cd1e5Scott Kennedy public Editor putString(String key, @Nullable String value) { 310d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 311d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.put(key, value); 312d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 313d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 314d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 3152333e3406c1742d0755b4a80daaf717aa50cd1e5Scott Kennedy public Editor putStringSet(String key, @Nullable Set<String> values) { 316d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 31701ed79c5786c527628544828abf8b70d02b989cdChristopher Tate mModified.put(key, 31801ed79c5786c527628544828abf8b70d02b989cdChristopher Tate (values == null) ? null : new HashSet<String>(values)); 319d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 320d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 321d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 322d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor putInt(String key, int value) { 323d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 324d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.put(key, value); 325d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 326d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 327d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 328d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor putLong(String key, long value) { 329d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 330d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.put(key, value); 331d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 332d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 333d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 334d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor putFloat(String key, float value) { 335d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 336d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.put(key, value); 337d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 338d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 339d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 340d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor putBoolean(String key, boolean value) { 341d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 342d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.put(key, value); 343d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 344d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 345d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 346d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 347d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor remove(String key) { 348d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 349d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.put(key, this); 350d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 351d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 352d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 353d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 354d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public Editor clear() { 355d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 356d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mClear = true; 357d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return this; 358d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 359d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 360d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 361d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void apply() { 362d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick final MemoryCommitResult mcr = commitToMemory(); 363d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick final Runnable awaitCommit = new Runnable() { 364d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void run() { 365d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick try { 366d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.writtenToDiskLatch.await(); 367d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } catch (InterruptedException ignored) { 368d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 369d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 370d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick }; 371d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 372d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick QueuedWork.add(awaitCommit); 373d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 374d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Runnable postWriteRunnable = new Runnable() { 375d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void run() { 376d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick awaitCommit.run(); 377d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick QueuedWork.remove(awaitCommit); 378d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 379d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick }; 380d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 381d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable); 382d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 383d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Okay to notify the listeners before it's hit disk 384d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // because the listeners should always get the same 385d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // SharedPreferences instance back, which has the 386d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // changes reflected in memory. 387d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick notifyListeners(mcr); 388d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 389d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 390d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Returns true if any changes were made 391d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private MemoryCommitResult commitToMemory() { 392d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick MemoryCommitResult mcr = new MemoryCommitResult(); 393d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (SharedPreferencesImpl.this) { 394d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // We optimistically don't make a deep copy until 395d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // a memory commit comes in when we're already 396d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // writing to disk. 397d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (mDiskWritesInFlight > 0) { 398d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // We can't modify our mMap as a currently 399d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // in-flight write owns it. Clone it before 400d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // modifying it. 401d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // noinspection unchecked 402d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mMap = new HashMap<String, Object>(mMap); 403d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 404d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.mapToWriteToDisk = mMap; 405d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mDiskWritesInFlight++; 406d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 407d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick boolean hasListeners = mListeners.size() > 0; 408d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (hasListeners) { 409d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.keysModified = new ArrayList<String>(); 410d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.listeners = 411d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet()); 412d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 413d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 414d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 415d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (mClear) { 416d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!mMap.isEmpty()) { 417d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.changesMade = true; 418d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mMap.clear(); 419d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 420d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mClear = false; 421d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 422d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 423d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick for (Map.Entry<String, Object> e : mModified.entrySet()) { 424d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick String k = e.getKey(); 425d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Object v = e.getValue(); 426c6f429007c2c402be71f69693cb1d2e6a1c8d0cfNarayan Kamath // "this" is the magic value for a removal mutation. In addition, 427c6f429007c2c402be71f69693cb1d2e6a1c8d0cfNarayan Kamath // setting a value to "null" for a given key is specified to be 428c6f429007c2c402be71f69693cb1d2e6a1c8d0cfNarayan Kamath // equivalent to calling remove on that key. 429c6f429007c2c402be71f69693cb1d2e6a1c8d0cfNarayan Kamath if (v == this || v == null) { 430d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!mMap.containsKey(k)) { 431d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick continue; 432d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 433d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mMap.remove(k); 434d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } else { 435d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (mMap.containsKey(k)) { 436d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Object existingValue = mMap.get(k); 437d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (existingValue != null && existingValue.equals(v)) { 438d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick continue; 439d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 440d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 441d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mMap.put(k, v); 442d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 443d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 444d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.changesMade = true; 445d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (hasListeners) { 446d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.keysModified.add(k); 447d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 448d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 449d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 450d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mModified.clear(); 451d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 452d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 453d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return mcr; 454d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 455d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 456d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public boolean commit() { 457d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick MemoryCommitResult mcr = commitToMemory(); 458d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick SharedPreferencesImpl.this.enqueueDiskWrite( 459d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr, null /* sync write on this thread okay */); 460d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick try { 461d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.writtenToDiskLatch.await(); 462d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } catch (InterruptedException e) { 463d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return false; 464d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 465d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick notifyListeners(mcr); 466d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return mcr.writeToDiskResult; 467d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 468d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 469d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private void notifyListeners(final MemoryCommitResult mcr) { 470d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (mcr.listeners == null || mcr.keysModified == null || 471d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.keysModified.size() == 0) { 472d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return; 473d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 474d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (Looper.myLooper() == Looper.getMainLooper()) { 475d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick for (int i = mcr.keysModified.size() - 1; i >= 0; i--) { 476d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick final String key = mcr.keysModified.get(i); 477d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick for (OnSharedPreferenceChangeListener listener : mcr.listeners) { 478d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (listener != null) { 479d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick listener.onSharedPreferenceChanged(SharedPreferencesImpl.this, key); 480d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 481d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 482d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 483d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } else { 484d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Run this function on the main thread. 485d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick ActivityThread.sMainThreadHandler.post(new Runnable() { 486d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void run() { 487d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick notifyListeners(mcr); 488d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 489d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick }); 490d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 491d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 492d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 493d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 494d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick /** 495d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * Enqueue an already-committed-to-memory result to be written 496d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * to disk. 497d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * 498d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * They will be written to disk one-at-a-time in the order 499d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * that they're enqueued. 500d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * 501d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * @param postWriteRunnable if non-null, we're being called 502d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * from apply() and this is the runnable to run after 503d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * the write proceeds. if null (from a regular commit()), 504d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * then we're allowed to do this disk write on the main 505d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * thread (which in addition to reducing allocations and 506d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * creating a background thread, this has the advantage that 507d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * we catch them in userdebug StrictMode reports to convert 508d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick * them where possible to apply() ...) 509d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick */ 510d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private void enqueueDiskWrite(final MemoryCommitResult mcr, 511d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick final Runnable postWriteRunnable) { 512d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick final Runnable writeToDiskRunnable = new Runnable() { 513d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick public void run() { 514d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (mWritingToDiskLock) { 515d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick writeToFile(mcr); 516d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 517d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (SharedPreferencesImpl.this) { 518d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mDiskWritesInFlight--; 519d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 520d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (postWriteRunnable != null) { 521d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick postWriteRunnable.run(); 522d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 523d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 524d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick }; 525d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 526d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick final boolean isFromSyncCommit = (postWriteRunnable == null); 527d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 528d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Typical #commit() path with fewer allocations, doing a write on 529d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // the current thread. 530d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (isFromSyncCommit) { 531d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick boolean wasEmpty = false; 532d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (SharedPreferencesImpl.this) { 533d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick wasEmpty = mDiskWritesInFlight == 1; 534d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 535d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (wasEmpty) { 536d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick writeToDiskRunnable.run(); 537d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return; 538d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 539d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 540d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 541d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable); 542d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 543d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 544d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private static FileOutputStream createFileOutputStream(File file) { 545d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick FileOutputStream str = null; 546d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick try { 547d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick str = new FileOutputStream(file); 548d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } catch (FileNotFoundException e) { 549d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick File parent = file.getParentFile(); 550d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!parent.mkdir()) { 551d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Log.e(TAG, "Couldn't create directory for SharedPreferences file " + file); 552d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return null; 553d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 554d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick FileUtils.setPermissions( 555d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick parent.getPath(), 556d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, 557d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick -1, -1); 558d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick try { 559d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick str = new FileOutputStream(file); 560d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } catch (FileNotFoundException e2) { 561d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Log.e(TAG, "Couldn't create SharedPreferences file " + file, e2); 562d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 563d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 564d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return str; 565d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 566d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 567d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Note: must hold mWritingToDiskLock 568d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick private void writeToFile(MemoryCommitResult mcr) { 569d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Rename the current file so it may be used as a backup during the next read 570d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (mFile.exists()) { 571d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!mcr.changesMade) { 572d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // If the file already exists, but no changes were 573d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // made to the underlying map, it's wasteful to 574d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // re-write the file. Return as if we wrote it 575d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // out. 576d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.setDiskWriteResult(true); 577d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return; 578d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 579d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!mBackupFile.exists()) { 580d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!mFile.renameTo(mBackupFile)) { 581d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Log.e(TAG, "Couldn't rename file " + mFile 582d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick + " to backup file " + mBackupFile); 583d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.setDiskWriteResult(false); 584d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return; 585d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 586d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } else { 587d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mFile.delete(); 588d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 589d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 590d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick 591d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Attempt to write the file, delete the backup and return true as atomically as 592d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // possible. If any exception occurs, delete the new file; next time we will restore 593d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // from the backup. 594d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick try { 595d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick FileOutputStream str = createFileOutputStream(mFile); 596d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (str == null) { 597d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.setDiskWriteResult(false); 598d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return; 599d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 600d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str); 601d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick FileUtils.sync(str); 602d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick str.close(); 603d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0); 60498e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root try { 60534385d352da19805ae948215e2edbeedd16b7941Elliott Hughes final StructStat stat = Os.stat(mFile.getPath()); 606d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick synchronized (this) { 60798e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root mStatTimestamp = stat.st_mtime; 60898e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root mStatSize = stat.st_size; 609d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 61098e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root } catch (ErrnoException e) { 61198e15e78934a00cf46f2be55472b7fd7a39ac0deKenny Root // Do nothing 612d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 613d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Writing was successful, delete the backup file if there is one. 614d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mBackupFile.delete(); 615d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.setDiskWriteResult(true); 616d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick return; 617d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } catch (XmlPullParserException e) { 618d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Log.w(TAG, "writeToFile: Got exception:", e); 619d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } catch (IOException e) { 620d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Log.w(TAG, "writeToFile: Got exception:", e); 621d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 622d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick // Clean up an unsuccessfully written file 623d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (mFile.exists()) { 624d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick if (!mFile.delete()) { 625d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick Log.e(TAG, "Couldn't clean up partially-written file " + mFile); 626d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 627d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 628d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick mcr.setDiskWriteResult(false); 629d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick } 630d3da44032084b6dd280487280553dcdbd7933e3eBrad Fitzpatrick} 631