Make informational queries respect requested projection
Fixed:
- DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI
- StreamItems.CONTENT_LIMIT_URI
Also now ProviderStatus now throws when a non-existent column is requested.
Change-Id: I03c82c31fe78a30d4929335dfb8efc3bff3bc76b
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index 6de16df..8822c44 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -57,6 +57,7 @@
import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
import android.accounts.Account;
import android.accounts.AccountManager;
@@ -5253,9 +5254,8 @@
}
case STREAM_ITEMS_LIMIT: {
- MatrixCursor cursor = new MatrixCursor(new String[]{StreamItems.MAX_ITEMS}, 1);
- cursor.addRow(new Object[]{MAX_STREAM_ITEMS_PER_RAW_CONTACT});
- return cursor;
+ return buildSingleRowResult(projection, new String[] {StreamItems.MAX_ITEMS},
+ new Object[] {MAX_STREAM_ITEMS_PER_RAW_CONTACT});
}
case STREAM_ITEMS_PHOTOS: {
@@ -5283,11 +5283,9 @@
}
case PHOTO_DIMENSIONS: {
- MatrixCursor cursor = new MatrixCursor(
- new String[]{DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
- 1);
- cursor.addRow(new Object[]{mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim});
- return cursor;
+ return buildSingleRowResult(projection,
+ new String[] {DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
+ new Object[] {mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim});
}
case PHONES:
@@ -5848,7 +5846,9 @@
}
case PROVIDER_STATUS: {
- return queryProviderStatus(uri, projection);
+ return buildSingleRowResult(projection,
+ new String[] {ProviderStatus.STATUS, ProviderStatus.DATA1},
+ new Object[] {mProviderStatus, mEstimatedStorageRequirement});
}
case DIRECTORIES : {
@@ -5908,21 +5908,6 @@
return c;
}
- /**
- * Creates a single-row cursor containing the current status of the provider.
- */
- private Cursor queryProviderStatus(Uri uri, String[] projection) {
- MatrixCursor cursor = new MatrixCursor(projection);
- RowBuilder row = cursor.newRow();
- for (int i = 0; i < projection.length; i++) {
- if (ProviderStatus.STATUS.equals(projection[i])) {
- row.add(mProviderStatus);
- } else if (ProviderStatus.DATA1.equals(projection[i])) {
- row.add(mEstimatedStorageRequirement);
- }
- }
- return cursor;
- }
/**
* Runs the query with the supplied contact ID and lookup ID. If the query succeeds,
@@ -8131,6 +8116,39 @@
}
/**
+ * Create a single row cursor for a simple, informational queries, such as
+ * {@link ProviderStatus#CONTENT_URI}.
+ */
+ @VisibleForTesting
+ static Cursor buildSingleRowResult(String[] projection, String[] availableColumns,
+ Object[] data) {
+ Preconditions.checkArgument(availableColumns.length == data.length);
+ if (projection == null) {
+ projection = availableColumns;
+ }
+ final MatrixCursor c = new MatrixCursor(projection, 1);
+ final RowBuilder row = c.newRow();
+
+ // It's O(n^2), but it's okay because we only have a few columns.
+ for (int i = 0; i < c.getColumnCount(); i++) {
+ final String column = c.getColumnName(i);
+
+ boolean found = false;
+ for (int j = 0; j < availableColumns.length; j++) {
+ if (availableColumns[j].equals(column)) {
+ row.add(data[j]);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new IllegalArgumentException("Invalid column " + projection[i]);
+ }
+ }
+ return c;
+ }
+
+ /**
* @return the currently active {@link ContactsDatabaseHelper} for the current thread.
*/
@NeededForTesting
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index bef077c..c7f7dcb 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -6393,6 +6393,53 @@
cursor.close();
}
+ public void testBuildSingleRowResult() {
+ checkBuildSingleRowResult(
+ new String[] {"b"},
+ new String[] {"a", "b"},
+ new Integer[] {1, 2},
+ new Integer[] {2}
+ );
+
+ checkBuildSingleRowResult(
+ new String[] {"b", "a", "b"},
+ new String[] {"a", "b"},
+ new Integer[] {1, 2},
+ new Integer[] {2, 1, 2}
+ );
+
+ checkBuildSingleRowResult(
+ null, // all columns
+ new String[] {"a", "b"},
+ new Integer[] {1, 2},
+ new Integer[] {1, 2}
+ );
+
+ try {
+ // Access non-existent column
+ ContactsProvider2.buildSingleRowResult(new String[] {"a"}, new String[] {"b"},
+ new Object[] {1});
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ private void checkBuildSingleRowResult(String[] projection, String[] availableColumns,
+ Object[] data, Integer[] expectedValues) {
+ final Cursor c = ContactsProvider2.buildSingleRowResult(projection, availableColumns, data);
+ try {
+ assertTrue(c.moveToFirst());
+ assertEquals(1, c.getCount());
+ assertEquals(expectedValues.length, c.getColumnCount());
+
+ for (int i = 0; i < expectedValues.length; i++) {
+ assertEquals("column " + i, expectedValues[i], (Integer) c.getInt(i));
+ }
+ } finally {
+ c.close();
+ }
+ }
+
private Cursor queryGroupMemberships(Account account) {
Cursor c = mResolver.query(maybeAddAccountQueryParameters(Data.CONTENT_URI, account),
new String[]{GroupMembership.GROUP_ROW_ID, GroupMembership.RAW_CONTACT_ID},