Modify metadata provider to support query other tables.

Metadata sync adapter needs to query raw contact, data, data usage
and aggregation exceptions tables to get metadata related info.

BUG 23041918

Change-Id: I387c7d592126ad5abc297863c99a5660e888a82c
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8017966..0b3b92f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -60,7 +60,7 @@
         </provider>
 
         <provider android:name="ContactMetadataProvider"
-                  android:authorities="com.android.contacts.metadata"
+                  android:authorities="com.android.contacts;com.android.contacts.metadata"
                   android:multiprocess="false"
                   android:exported="true"
                   android:permission="android.permission.READ_WRITE_CONTACT_METADATA">
diff --git a/src/com/android/providers/contacts/ContactMetadataProvider.java b/src/com/android/providers/contacts/ContactMetadataProvider.java
index eff13fc..328181e 100644
--- a/src/com/android/providers/contacts/ContactMetadataProvider.java
+++ b/src/com/android/providers/contacts/ContactMetadataProvider.java
@@ -58,12 +58,23 @@
     private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
     private static final int METADATA_SYNC = 1;
     private static final int METADATA_SYNC_ID = 2;
+    private static final int RAW_CONTACTS = 3;
+    private static final int RAW_CONTACTS_ID = 4;
+    private static final int DATA = 5;
+    private static final int DATA_ID = 6;
+    private static final int AGGREGATION_EXCEPTIONS = 7;
 
     private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 
     static {
         sURIMatcher.addURI(MetadataSync.METADATA_AUTHORITY, "metadata_sync", METADATA_SYNC);
         sURIMatcher.addURI(MetadataSync.METADATA_AUTHORITY, "metadata_sync/#", METADATA_SYNC_ID);
+        sURIMatcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
+        sURIMatcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
+        sURIMatcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
+        sURIMatcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
+        sURIMatcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
+                AGGREGATION_EXCEPTIONS);
     }
 
     private static final Map<String, String> sMetadataProjectionMap = ProjectionMap.builder()
@@ -129,6 +140,15 @@
                         ContentUris.parseId(uri)));
                 break;
             }
+
+            case RAW_CONTACTS:
+            case RAW_CONTACTS_ID:
+            case DATA: // Includes data usage stats query.
+            case DATA_ID:
+            case AGGREGATION_EXCEPTIONS:
+                return mContactsProvider.query(
+                        uri, projection, selection, selectionArgs, sortOrder);
+
             default:
                 throw new IllegalArgumentException("Unknown URL " + uri);
         }
@@ -146,6 +166,16 @@
                 return MetadataSync.CONTENT_TYPE;
             case METADATA_SYNC_ID:
                 return MetadataSync.CONTENT_ITEM_TYPE;
+            case RAW_CONTACTS:
+                return ContactsContract.RawContacts.CONTENT_TYPE;
+            case RAW_CONTACTS_ID:
+                return ContactsContract.RawContacts.CONTENT_ITEM_TYPE;
+            case DATA:
+                return ContactsContract.Data.CONTENT_TYPE;
+            case DATA_ID:
+                return mContactsProvider.getType(uri);
+            case AGGREGATION_EXCEPTIONS:
+                return ContactsContract.AggregationExceptions.CONTENT_TYPE;
             default:
                 throw new IllegalArgumentException("Unknown URI: " + uri);
         }
diff --git a/tests/src/com/android/providers/contacts/ContactMetadataProviderTest.java b/tests/src/com/android/providers/contacts/ContactMetadataProviderTest.java
index e48e2a6..d5e4042 100644
--- a/tests/src/com/android/providers/contacts/ContactMetadataProviderTest.java
+++ b/tests/src/com/android/providers/contacts/ContactMetadataProviderTest.java
@@ -21,6 +21,10 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.AggregationExceptions;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.DataUsageFeedback;
 import android.provider.ContactsContract.MetadataSync;
 import android.provider.ContactsContract.RawContacts;
 import android.test.MoreAsserts;
@@ -169,6 +173,99 @@
         }
     }
 
+    public void testQueryRawContact() {
+        // Create a raw contact.
+        long rawContactId = RawContactUtil.createRawContactWithAccountDataSet(
+                mResolver, mTestAccount);
+        Cursor c = mContactMetadataProvider.query(RawContacts.CONTENT_URI,
+                new String[]{RawContacts._ID}, RawContacts._ID + "=?",
+                new String[]{String.valueOf(rawContactId)}, null);
+        assertEquals(1, c.getCount());
+    }
+
+    public void testQueryData() {
+        // Create a raw contact.
+        long rawContactId = RawContactUtil.createRawContactWithAccountDataSet(
+                mResolver, mTestAccount);
+        // Create a data for the raw contact.
+        ContentValues values = new ContentValues();
+        values.put(Data.RAW_CONTACT_ID, rawContactId);
+        values.put(Data.MIMETYPE, "testmimetype");
+        values.put(Data.RES_PACKAGE, "oldpackage");
+        values.put(Data.IS_PRIMARY, 1);
+        values.put(Data.IS_SUPER_PRIMARY, 1);
+        values.put(Data.DATA1, "one");
+        values.put(Data.DATA2, "two");
+        values.put(Data.DATA3, "three");
+        values.put(Data.DATA4, "four");
+        values.put(Data.DATA5, "five");
+        values.put(Data.DATA6, "six");
+        values.put(Data.DATA7, "seven");
+        values.put(Data.DATA8, "eight");
+        values.put(Data.DATA9, "nine");
+        values.put(Data.DATA10, "ten");
+        values.put(Data.DATA11, "eleven");
+        values.put(Data.DATA12, "twelve");
+        values.put(Data.DATA13, "thirteen");
+        values.put(Data.DATA14, "fourteen");
+        values.put(Data.DATA15, "fifteen".getBytes());
+        values.put(Data.CARRIER_PRESENCE, ContactsContract.Data.CARRIER_PRESENCE_VT_CAPABLE);
+        values.put(Data.SYNC1, "sync1");
+        values.put(Data.SYNC2, "sync2");
+        values.put(Data.SYNC3, "sync3");
+        values.put(Data.SYNC4, "sync4");
+        Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
+        long dataId = ContentUris.parseId(dataUri);
+        Uri dataUsageUri = ContactsContract.DataUsageFeedback.FEEDBACK_URI.buildUpon()
+                .appendPath(String.valueOf(dataId))
+                .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
+                        DataUsageFeedback.USAGE_TYPE_CALL)
+                .build();
+        assertEquals(1, mResolver.update(dataUsageUri, new ContentValues(), null, null));
+
+        // Update data usage for dataId, times used changes from 0 to 1 for "CALL" type.
+        Uri uri1 = Data.CONTENT_URI.buildUpon()
+                .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
+                        DataUsageFeedback.USAGE_TYPE_CALL)
+                .build();
+        Cursor c1 = mContactMetadataProvider.query(uri1,
+                new String[] {Data.HASH_ID, Data.IS_PRIMARY, Data.IS_SUPER_PRIMARY,
+                        Data.LAST_TIME_USED, Data.TIMES_USED}, Data._ID + "=?",
+                new String[] {String.valueOf(dataId)}, null);
+        assertEquals(1, c1.getCount());
+        c1.moveToNext();
+        assertEquals(1, c1.getInt(4)); // times_used
+
+        Uri uri2 = Data.CONTENT_URI.buildUpon()
+                .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
+                        DataUsageFeedback.USAGE_TYPE_LONG_TEXT)
+                .build();
+        Cursor c2 = mContactMetadataProvider.query(uri2,
+                new String[] {Data.HASH_ID, Data.IS_PRIMARY, Data.IS_SUPER_PRIMARY,
+                        Data.LAST_TIME_USED, Data.TIMES_USED}, Data._ID + "=?",
+                new String[] {String.valueOf(dataId)}, null);
+        assertEquals(1, c2.getCount());
+        // Check if times used for "long text" type is still 0.
+        c2.moveToNext();
+        assertEquals(0, c2.getInt(4)); // times_used
+    }
+
+    public void testQueryAggregationExceptions() {
+        long rawContactId1 = RawContactUtil.createRawContactWithAccountDataSet(
+                mResolver, mTestAccount);
+        long rawContactId2 = RawContactUtil.createRawContactWithAccountDataSet(
+                mResolver, mTestAccount);
+        ContentValues values = new ContentValues();
+        values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
+        values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
+        values.put(AggregationExceptions.TYPE, 1/*TOGETHER*/);
+        mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null);
+
+        Cursor c = mContactMetadataProvider.query(AggregationExceptions.CONTENT_URI,
+                new String[] {ContactsContract.AggregationExceptions.TYPE}, null, null, null);
+        assertEquals(1, c.getCount());
+    }
+
     public void testInsertAndUpdateMetadataSync() {
         // Create a raw contact with backupId.
         String backupId = "backupId10001";