Merge cherrypicks of ['googleplex-android-review.googlesource.com/23764853', 'googleplex-android-review.googlesource.com/23916075'] into tm-qpr3-release.

Change-Id: I3876a1c36c3b39d116bf8afac671d4ca2967e051
diff --git a/src/com/android/providers/media/util/DatabaseUtils.java b/src/com/android/providers/media/util/DatabaseUtils.java
index 55efafc..53ecf96 100644
--- a/src/com/android/providers/media/util/DatabaseUtils.java
+++ b/src/com/android/providers/media/util/DatabaseUtils.java
@@ -127,8 +127,9 @@
                             res.append(((Boolean) arg).booleanValue() ? 1 : 0);
                         } else {
                             res.append('\'');
-                            // Escape single quote character while appending the string.
-                            res.append(arg.toString().replace("'", "''"));
+                            // Escape single quote character while appending the string and reject
+                            // invalid unicode.
+                            res.append(escapeSingleQuoteAndRejectInvalidUnicode(arg.toString()));
                             res.append('\'');
                         }
                         break;
@@ -142,6 +143,37 @@
         return res.toString();
     }
 
+    private static String escapeSingleQuoteAndRejectInvalidUnicode(@NonNull String target) {
+        final int len = target.length();
+        final StringBuilder res = new StringBuilder(len);
+        boolean lastHigh = false;
+
+        for (int i = 0; i < len; ) {
+            final char c = target.charAt(i++);
+
+            if (lastHigh != Character.isLowSurrogate(c)) {
+                Log.e(TAG, "Invalid surrogate in string " + target);
+                throw new IllegalArgumentException("Invalid surrogate in string " + target);
+            }
+
+            lastHigh = Character.isHighSurrogate(c);
+
+            // Escape the single quotes by duplicating them
+            if (c == '\'') {
+                res.append(c);
+            }
+
+            res.append(c);
+        }
+
+        if (lastHigh) {
+            Log.e(TAG, "Invalid surrogate in string " + target);
+            throw new IllegalArgumentException("Invalid surrogate in string " + target);
+        }
+
+        return res.toString();
+    }
+
     /**
      * Returns data type of the given object's value.
      *<p>
diff --git a/src/com/android/providers/media/util/FileUtils.java b/src/com/android/providers/media/util/FileUtils.java
index 2e2a3e7..097eca8 100644
--- a/src/com/android/providers/media/util/FileUtils.java
+++ b/src/com/android/providers/media/util/FileUtils.java
@@ -1324,9 +1324,17 @@
         values.remove(MediaColumns.BUCKET_ID);
         values.remove(MediaColumns.BUCKET_DISPLAY_NAME);
 
-        final String data = values.getAsString(MediaColumns.DATA);
+        String data = values.getAsString(MediaColumns.DATA);
         if (TextUtils.isEmpty(data)) return;
 
+        try {
+            data = new File(data).getCanonicalPath();
+            values.put(MediaColumns.DATA, data);
+        } catch (IOException e) {
+            throw new IllegalArgumentException(
+                    String.format(Locale.ROOT, "Invalid file path:%s in request.", data));
+        }
+
         final File file = new File(data);
         final File fileLower = new File(data.toLowerCase(Locale.ROOT));
 
diff --git a/tests/src/com/android/providers/media/util/DatabaseUtilsTest.java b/tests/src/com/android/providers/media/util/DatabaseUtilsTest.java
index 685d897..a907875 100644
--- a/tests/src/com/android/providers/media/util/DatabaseUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/DatabaseUtilsTest.java
@@ -39,6 +39,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -127,6 +128,15 @@
     }
 
     @Test
+    public void testBindSelection_RejectInvalidUnicode() {
+        assertThrows(IllegalArgumentException.class, () -> bindSelection("DATA=?", "Fo\uD83Do"));
+        assertThrows(IllegalArgumentException.class, () -> bindSelection("DATA=?", "Fo\uDE00o"));
+        assertEquals("DATA='Fo\uD83D\uDE00o'", bindSelection("DATA=?", "Fo\uD83D\uDE00o"));
+        assertThrows(
+                IllegalArgumentException.class, () -> bindSelection("DATA=?", "Fo\uDE00\uD83Do"));
+    }
+
+    @Test
     public void testResolveQueryArgs_GroupBy() throws Exception {
         args.putStringArray(QUERY_ARG_GROUP_COLUMNS, new String[] { "foo", "bar" });
         args.putString(QUERY_ARG_SQL_GROUP_BY, "raw");