Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 342984a1 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick
Browse files

SettingsProvider: defensively cap size of settings kept cached in memory.

Change-Id: I50289ece2d7f5f50d2ea2efbacac7a0bb1483bf6
parent 104798b7
Loading
Loading
Loading
Loading
+28 −12
Original line number Diff line number Diff line
@@ -60,6 +60,11 @@ public class SettingsProvider extends ContentProvider {
    private static final SettingsCache sSystemCache = new SettingsCache();
    private static final SettingsCache sSecureCache = new SettingsCache();

    // Over this size we don't reject loading or saving settings but
    // we do consider them broken/malicious and don't keep them in
    // memory at least:
    private static final int MAX_CACHE_ENTRY_SIZE = 500;

    private static final Bundle NULL_SETTING = Bundle.forPair("value", null);

    protected DatabaseHelper mOpenHelper;
@@ -264,10 +269,7 @@ public class SettingsProvider extends ContentProvider {
                              null, null, null, null);
            if (cursor != null && cursor.getCount() == 1) {
                cursor.moveToFirst();
                String value = cursor.getString(0);
                Bundle bundle = (value == null) ? NULL_SETTING : Bundle.forPair("value", value);
                cache.putIfAbsentLocked(key, bundle);
                return bundle;
                return cache.putIfAbsent(key, cursor.getString(0));
            }
        } catch (SQLiteException e) {
            Log.w(TAG, "settings lookup error", e);
@@ -275,7 +277,7 @@ public class SettingsProvider extends ContentProvider {
        } finally {
            if (cursor != null) cursor.close();
        }
        cache.putIfAbsentLocked(key, NULL_SETTING);
        cache.putIfAbsent(key, null);
        return NULL_SETTING;
    }

@@ -592,6 +594,7 @@ public class SettingsProvider extends ContentProvider {
     * database.
     */
    private static final class SettingsCache extends LinkedHashMap<String, Bundle> {

        public SettingsCache() {
            super(MAX_CACHE_ENTRIES, 0.75f /* load factor */, true /* access ordered */);
        }
@@ -601,14 +604,23 @@ public class SettingsProvider extends ContentProvider {
            return size() > MAX_CACHE_ENTRIES;
        }

        public void putIfAbsentLocked(String key, Bundle value) {
        /**
         * Atomic cache population, conditional on size of value and if
         * we lost a race.
         *
         * @returns a Bundle to send back to the client from call(), even
         *     if we lost the race.
         */
        public Bundle putIfAbsent(String key, String value) {
            Bundle bundle = (value == null) ? NULL_SETTING : Bundle.forPair("value", value);
            if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) {
                synchronized (this) {
                if (containsKey(key)) {
                    // Lost a race.
                    return;
                    if (!containsKey(key)) {
                        put(key, bundle);
                    }
                }
                put(key, value);
            }
            return bundle;
        }

        public static SettingsCache forTable(String tableName) {
@@ -635,7 +647,11 @@ public class SettingsProvider extends ContentProvider {
            }
            String value = contentValues.getAsString(Settings.NameValueTable.VALUE);
            synchronized (cache) {
                if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) {
                    cache.put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value));
                } else {
                    cache.remove(name);
                }
            }
        }