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");