Notify focus sync adapter for contact metadata change.

Notify focus sync adapter and also mark its raw contact as dirty
if there is contact metadata change.
For metadata change, starred,pinned,send_to_voicemail,data usage,
aggregation is covered in this cl. For data primary info, it has
been processed in the old logic.

BUG 27301947

Change-Id: I5ffda518f345a6174b38cfacf7df7cf294cfaa04
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index 3efc84a..6414fbd 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -2891,6 +2891,8 @@
         if (needToUpdateMetadata) {
             mTransactionContext.get().markRawContactMetadataDirty(rawContactId,
                     /* isMetadataSyncAdapter =*/false);
+            mTransactionContext.get().markRawContactDirtyAndChanged(
+                    rawContactId, callerIsSyncAdapter);
         }
         // If the new raw contact is inserted by a sync adapter, mark mSyncToMetadataNetWork as true
         // so that it can trigger the metadata syncing from the server.
@@ -4049,6 +4051,9 @@
             case PROFILE: {
                 invalidateFastScrollingIndexCache();
                 count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
+                if (count > 0) {
+                    mSyncToNetwork |= !callerIsSyncAdapter;
+                }
                 break;
             }
 
@@ -4056,6 +4061,9 @@
                 invalidateFastScrollingIndexCache();
                 count = updateContactOptions(db, ContentUris.parseId(uri), values,
                         callerIsSyncAdapter);
+                if (count > 0) {
+                    mSyncToNetwork |= !callerIsSyncAdapter;
+                }
                 break;
             }
 
@@ -4118,6 +4126,9 @@
                 invalidateFastScrollingIndexCache();
                 selection = appendAccountIdToSelection(uri, selection);
                 count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
+                if (count > 0) {
+                    mSyncToNetwork |= !callerIsSyncAdapter;
+                }
                 break;
             }
 
@@ -4134,6 +4145,9 @@
                     count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
                             callerIsSyncAdapter);
                 }
+                if (count > 0) {
+                    mSyncToNetwork |= !callerIsSyncAdapter;
+                }
                 break;
             }
 
@@ -4159,9 +4173,12 @@
             }
 
             case AGGREGATION_EXCEPTIONS: {
-                count = updateAggregationException(db, values,
+                count = updateAggregationException(db, values, callerIsSyncAdapter,
                         /* callerIsMetadataSyncAdapter =*/false);
                 invalidateFastScrollingIndexCache();
+                if (count > 0) {
+                    mSyncToNetwork |= !callerIsSyncAdapter;
+                }
                 break;
             }
 
@@ -4227,7 +4244,10 @@
             }
 
             case DATA_USAGE_FEEDBACK_ID: {
-                count = handleDataUsageFeedback(uri) ? 1 : 0;
+                count = handleDataUsageFeedback(uri, callerIsSyncAdapter) ? 1 : 0;
+                if (count > 0) {
+                    mSyncToNetwork |= !callerIsSyncAdapter;
+                }
                 break;
             }
 
@@ -4562,6 +4582,8 @@
             if (shouldMarkMetadataDirtyForRawContact(values)) {
                 mTransactionContext.get().markRawContactMetadataDirty(
                         rawContactId, callerIsMetadataSyncAdapter);
+                mTransactionContext.get().markRawContactDirtyAndChanged(
+                        rawContactId, callerIsSyncAdapter);
             }
             if (isBackupIdChanging) {
                 Cursor cursor = db.query(Tables.RAW_CONTACTS,
@@ -4746,11 +4768,9 @@
         final boolean hasStarredValue = flagExists(values, RawContacts.STARRED);
         final boolean hasPinnedValue = flagExists(values, RawContacts.PINNED);
         final boolean hasVoiceMailValue = flagExists(values, RawContacts.SEND_TO_VOICEMAIL);
-        if (hasStarredValue) {
+        if (hasStarredValue || hasPinnedValue || hasVoiceMailValue) {
             // Mark dirty when changing starred to trigger sync.
             values.put(RawContacts.DIRTY, 1);
-        }
-        if (hasStarredValue || hasPinnedValue || hasVoiceMailValue) {
             // Mark dirty to trigger metadata syncing.
             values.put(RawContacts.METADATA_DIRTY, 1);
         }
@@ -4818,7 +4838,7 @@
     }
 
     private int updateAggregationException(SQLiteDatabase db, ContentValues values,
-            boolean callerIsMetadataSyncAdapter) {
+            boolean callerIsSyncAdapter, boolean callerIsMetadataSyncAdapter) {
         Integer exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
         Long rcId1 = values.getAsLong(AggregationExceptions.RAW_CONTACT_ID1);
         Long rcId2 = values.getAsLong(AggregationExceptions.RAW_CONTACT_ID2);
@@ -4862,6 +4882,11 @@
         mTransactionContext.get().markRawContactMetadataDirty(rawContactId2,
                 callerIsMetadataSyncAdapter);
 
+        mTransactionContext.get().markRawContactDirtyAndChanged(rawContactId1,
+                callerIsSyncAdapter);
+        mTransactionContext.get().markRawContactDirtyAndChanged(rawContactId2,
+                callerIsSyncAdapter);
+
         // The return value is fake - we just confirm that we made a change, not count actual
         // rows changed.
         return 1;
@@ -5069,7 +5094,8 @@
             values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
             values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
             values.put(AggregationExceptions.TYPE, typeInt);
-            updateAggregationException(db, values, /* callerIsMetadataSyncAdapter =*/true);
+            updateAggregationException(db, values, /*callerIsSyncAdapter=*/true,
+                    /* callerIsMetadataSyncAdapter =*/true);
             if (rawContactId1 != rawContactId) {
                 aggregationRawContactIdsInServer.add(rawContactId1);
             }
@@ -5087,7 +5113,8 @@
             values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId);
             values.put(AggregationExceptions.RAW_CONTACT_ID2, deleteRawContactId);
             values.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE_AUTOMATIC);
-            updateAggregationException(db, values, /* callerIsMetadataSyncAdapter =*/true);
+            updateAggregationException(db, values, /*callerIsSyncAdapter=*/true,
+                    /* callerIsMetadataSyncAdapter =*/true);
         }
     }
 
@@ -9676,7 +9703,7 @@
         db.execSQL(UNDEMOTE_RAW_CONTACT, arg);
     }
 
-    private boolean handleDataUsageFeedback(Uri uri) {
+    private boolean handleDataUsageFeedback(Uri uri, boolean callerIsSyncAdapter) {
         final long currentTimeMillis = Clock.getInstance().currentTimeMillis();
         final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
         final String[] ids = uri.getLastPathSegment().trim().split(",");
@@ -9715,6 +9742,7 @@
                 final long rid =   cursor.getLong(0);
                 mTransactionContext.get().markRawContactMetadataDirty(rid,
                         /* isMetadataSyncAdapter =*/false);
+                mTransactionContext.get().markRawContactDirtyAndChanged(rid, callerIsSyncAdapter);
                 rawContactIds.add(rid);
             }
         } finally {
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index 07fd8dc..f432b0b 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -5230,11 +5230,13 @@
 
         updateSendToVoicemailAndRingtone(contactId, true, "foo");
         assertSendToVoicemailAndRingtone(contactId, true, "foo");
-        assertNetworkNotified(false);
+        assertNetworkNotified(true);
+        assertDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), true);
 
         updateSendToVoicemailAndRingtoneWithSelection(contactId, false, "bar");
         assertSendToVoicemailAndRingtone(contactId, false, "bar");
-        assertNetworkNotified(false);
+        assertNetworkNotified(true);
+        assertDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), true);
     }
 
     public void testSendToVoicemailAndRingtoneAfterAggregation() {
@@ -5292,6 +5294,19 @@
         assertSendToVoicemailAndRingtone(queryContactId(rawContactId2), false, "bar");
     }
 
+    public void testMarkDirtyAfterAggregation() {
+        long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "i", "j");
+        long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "k", "l");
+
+        // Aggregate them
+        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
+                rawContactId1, rawContactId2);
+
+        assertDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1), true);
+        assertDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2), true);
+        assertNetworkNotified(true);
+    }
+
     public void testStatusUpdateInsert() {
         long rawContactId = RawContactUtil.createRawContact(mResolver);
         Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
@@ -6875,6 +6890,21 @@
         assertNetworkNotified(false);
     }
 
+    public void testDirtyWhenRawContactInsert() {
+        long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
+        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
+        assertDirty(rawContactUri, false);
+        assertNetworkNotified(true);
+
+        ContentValues values = new ContentValues();
+        values.put(ContactsContract.RawContacts.STARRED, 1);
+        values.put(ContactsContract.RawContacts.ACCOUNT_NAME, mAccount.name);
+        values.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccount.type);
+        Uri rawContactId2Uri = mResolver.insert(RawContacts.CONTENT_URI, values);
+        assertDirty(rawContactId2Uri, true);
+        assertNetworkNotified(true);
+    }
+
     public void testRawContactDirtyAndVersion() {
         final long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
         Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId);
@@ -6890,8 +6920,9 @@
         assertEquals(1, mResolver.update(uri, values, null, null));
         assertEquals(version, getVersion(uri));
 
-        assertDirty(uri, false);
-        assertNetworkNotified(false);
+        // Mark dirty when send_to_voicemail/starred was set.
+        assertDirty(uri, true);
+        assertNetworkNotified(true);
 
         Uri emailUri = insertEmail(rawContactId, "goo@woo.com");
         assertDirty(uri, true);
@@ -8648,6 +8679,15 @@
         }
     }
 
+    public void testMarkDirtyWhenDataUsageUpdate() {
+        final long rid1 = RawContactUtil.createRawContactWithName(mResolver, "contact", "a");
+        final long did1a = ContentUris.parseId(insertEmail(rid1, "email_1_a@email.com"));
+        updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1a);
+
+        assertDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rid1), true);
+        assertNetworkNotified(true);
+    }
+
     public void testDataUsageFeedbackAndDelete() {
 
         sMockClock.install();
@@ -8854,6 +8894,17 @@
         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
     }
 
+    public void testContactUpdate_dirtyForMetadataChange() {
+        DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
+
+        ContentValues values = new ContentValues();
+        values.put(Contacts.PINNED, 1);
+
+        ContactUtil.update(mResolver, ids.mContactId, values);
+        assertDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, ids.mRawContactId), true);
+        assertNetworkNotified(true);
+    }
+
     public void testContactUpdate_updatesContactUpdatedTimestamp() {
         sMockClock.install();
         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);