summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Andreas Gampe <agampe@google.com> 2018-01-03 17:36:20 -0800
committer Andreas Gampe <agampe@google.com> 2018-01-05 18:02:30 -0800
commit1aa0d01cc859ba0a99e373759fab634f0ae9b17a (patch)
tree4768d1e45d0eb1e0606181b296286d360f5cd4d8
parentc6dd5f9037bf5f9414ac23983d8a69291904102a (diff)
Frameworks: Move SharedPreferencesImpl to tristate
The code loading shared preferences from disk may throw in other ways then a missing file, for example during an out-of-memory situation. Ensure that waiters will be woken up, and propagate the exception to the getters. Bug: 67986472 Test: m Test: Device boots Test: m cts && cts-tradefed run commandAndExit cts-dev --module CtsContentTestCases -c android.content.cts.SharedPreferencesTest Change-Id: I56e47342111f1cb53c5817c0429af4a6787475a9
-rw-r--r--core/java/android/app/SharedPreferencesImpl.java43
1 files changed, 34 insertions, 9 deletions
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 8c47598fff34..6ac15a5f8c91 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -71,6 +71,8 @@ final class SharedPreferencesImpl implements SharedPreferences {
@GuardedBy("mLock")
private Map<String, Object> mMap;
+ @GuardedBy("mLock")
+ private Throwable mThrowable;
@GuardedBy("mLock")
private int mDiskWritesInFlight = 0;
@@ -107,6 +109,7 @@ final class SharedPreferencesImpl implements SharedPreferences {
mMode = mode;
mLoaded = false;
mMap = null;
+ mThrowable = null;
startLoadFromDisk();
}
@@ -139,13 +142,14 @@ final class SharedPreferencesImpl implements SharedPreferences {
Map<String, Object> map = null;
StructStat stat = null;
+ Throwable thrown = null;
try {
stat = Os.stat(mFile.getPath());
if (mFile.canRead()) {
BufferedInputStream str = null;
try {
str = new BufferedInputStream(
- new FileInputStream(mFile), 16*1024);
+ new FileInputStream(mFile), 16 * 1024);
map = (Map<String, Object>) XmlUtils.readMapXml(str);
} catch (Exception e) {
Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
@@ -154,19 +158,36 @@ final class SharedPreferencesImpl implements SharedPreferences {
}
}
} catch (ErrnoException e) {
- /* ignore */
+ // An errno exception means the stat failed. Treat as empty/non-existing by
+ // ignoring.
+ } catch (Throwable t) {
+ thrown = t;
}
synchronized (mLock) {
mLoaded = true;
- if (map != null) {
- mMap = map;
- mStatTimestamp = stat.st_mtim;
- mStatSize = stat.st_size;
- } else {
- mMap = new HashMap<>();
+ mThrowable = thrown;
+
+ // It's important that we always signal waiters, even if we'll make
+ // them fail with an exception. The try-finally is pretty wide, but
+ // better safe than sorry.
+ try {
+ if (thrown == null) {
+ if (map != null) {
+ mMap = map;
+ mStatTimestamp = stat.st_mtim;
+ mStatSize = stat.st_size;
+ } else {
+ mMap = new HashMap<>();
+ }
+ }
+ // In case of a thrown exception, we retain the old map. That allows
+ // any open editors to commit and store updates.
+ } catch (Throwable t) {
+ mThrowable = t;
+ } finally {
+ mLock.notifyAll();
}
- mLock.notifyAll();
}
}
@@ -226,6 +247,7 @@ final class SharedPreferencesImpl implements SharedPreferences {
}
}
+ @GuardedBy("mLock")
private void awaitLoadedLocked() {
if (!mLoaded) {
// Raise an explicit StrictMode onReadFromDisk for this
@@ -239,6 +261,9 @@ final class SharedPreferencesImpl implements SharedPreferences {
} catch (InterruptedException unused) {
}
}
+ if (mThrowable != null) {
+ throw new IllegalStateException(mThrowable);
+ }
}
@Override