Allow binary value in SettingsProvider

Now a text value will be written to "value" but a binary value will be encoded
in base64 and stored in "valueBase64".

A null value will have neither value nor valueBase64.

Bug 20202004

Change-Id: I1eae936ff38e3460dc76ca20cc38f8d7e5ec6215
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 5137e1b..aff6ad8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -342,7 +342,7 @@
         }
 
         String name = values.getAsString(Settings.Secure.NAME);
-        if (TextUtils.isEmpty(name)) {
+        if (!isKeyValid(name)) {
             return null;
         }
 
@@ -406,11 +406,10 @@
             return 0;
         }
 
-        if (TextUtils.isEmpty(args.name)) {
+        if (!isKeyValid(args.name)) {
             return 0;
         }
 
-
         switch (args.table) {
             case TABLE_GLOBAL: {
                 final int userId = UserHandle.getCallingUserId();
@@ -446,10 +445,11 @@
             return 0;
         }
 
-        String value = values.getAsString(Settings.Secure.VALUE);
-        if (TextUtils.isEmpty(value)) {
+        String name = values.getAsString(Settings.Secure.NAME);
+        if (!isKeyValid(name)) {
             return 0;
         }
+        String value = values.getAsString(Settings.Secure.VALUE);
 
         switch (args.table) {
             case TABLE_GLOBAL: {
@@ -525,13 +525,20 @@
         final int valueColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.VALUE);
 
         do {
-            pw.append("_id:").append(cursor.getString(idColumnIdx));
-            pw.append(" name:").append(cursor.getString(nameColumnIdx));
-            pw.append(" value:").append(cursor.getString(valueColumnIdx));
+            pw.append("_id:").append(toDumpString(cursor.getString(idColumnIdx)));
+            pw.append(" name:").append(toDumpString(cursor.getString(nameColumnIdx)));
+            pw.append(" value:").append(toDumpString(cursor.getString(valueColumnIdx)));
             pw.println();
         } while (cursor.moveToNext());
     }
 
+    private static final String toDumpString(String s) {
+        if (s != null) {
+            return s;
+        }
+        return "{null}";
+    }
+
     private void registerBroadcastReceivers() {
         IntentFilter userFilter = new IntentFilter();
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
@@ -1280,6 +1287,10 @@
         cursor.addRow(values);
     }
 
+    private static boolean isKeyValid(String key) {
+        return !(TextUtils.isEmpty(key) || SettingsState.isBinary(key));
+    }
+
     private static final class Arguments {
         private static final Pattern WHERE_PATTERN_WITH_PARAM_NO_BRACKETS =
                 Pattern.compile("[\\s]*name[\\s]*=[\\s]*\\?[\\s]*");
@@ -1812,7 +1823,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 120;
+            private static final int SETTINGS_VERSION = 121;
 
             private final int mUserId;
 
@@ -1940,6 +1951,10 @@
                     currentVersion = 120;
                 }
 
+                // Before 121, we used a different string encoding logic.  We just bump the version
+                // here; SettingsState knows how to handle pre-version 120 files.
+                currentVersion = 121;
+
                 // vXXX: Add new settings above this point.
 
                 // Return the current version.