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

Change-Id: I50289ece2d7f5f50d2ea2efbacac7a0bb1483bf6
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 942d32d..83937fa 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -60,6 +60,11 @@
     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 @@
                               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 @@
         } finally {
             if (cursor != null) cursor.close();
         }
-        cache.putIfAbsentLocked(key, NULL_SETTING);
+        cache.putIfAbsent(key, null);
         return NULL_SETTING;
     }
 
@@ -592,6 +594,7 @@
      * 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 @@
             return size() > MAX_CACHE_ENTRIES;
         }
 
-        public void putIfAbsentLocked(String key, Bundle value) {
-            synchronized (this) {
-                if (containsKey(key)) {
-                    // Lost a race.
-                    return;
+        /**
+         * 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)) {
+                        put(key, bundle);
+                    }
                 }
-                put(key, value);
             }
+            return bundle;
         }
 
         public static SettingsCache forTable(String tableName) {
@@ -635,7 +647,11 @@
             }
             String value = contentValues.getAsString(Settings.NameValueTable.VALUE);
             synchronized (cache) {
-                cache.put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value));
+                if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) {
+                    cache.put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value));
+                } else {
+                    cache.remove(name);
+                }
             }
         }