diff options
5 files changed, 463 insertions, 53 deletions
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 5154900..a181f06 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -153,6 +153,11 @@ import com.android.providers.contacts.aggregation.util.CommonNicknameCache; import com.android.providers.contacts.database.ContactsTableUtil; import com.android.providers.contacts.database.DeletedContactsTableUtil; import com.android.providers.contacts.database.MoreDatabaseUtils; +import com.android.providers.contacts.MetadataEntryParser.AggregationData; +import com.android.providers.contacts.MetadataEntryParser.FieldData; +import com.android.providers.contacts.MetadataEntryParser.MetadataEntry; +import com.android.providers.contacts.MetadataEntryParser.RawContactInfo; +import com.android.providers.contacts.MetadataEntryParser.UsageStats; import com.android.providers.contacts.util.Clock; import com.android.providers.contacts.util.DbQueryUtils; import com.android.providers.contacts.util.NeededForTesting; @@ -4673,6 +4678,162 @@ public class ContactsProvider2 extends AbstractContactsProvider scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS); } + interface RawContactsBackupQuery { + String TABLE = Tables.RAW_CONTACTS; + String[] COLUMNS = new String[] { + RawContacts._ID, + }; + int RAW_CONTACT_ID = 0; + String SELECTION = RawContacts.DELETED + "=0 AND " + + RawContacts.BACKUP_ID + "=? AND " + + RawContactsColumns.ACCOUNT_ID + "=?"; + } + + /** + * Fetch rawContactId related to the given backupId. + * Return 0 if there's no such rawContact or it's deleted. + */ + private long queryRawContactId(SQLiteDatabase db, String backupId, long accountId) { + if (TextUtils.isEmpty(backupId)) { + return 0; + } + mSelectionArgs2[0] = backupId; + mSelectionArgs2[1] = String.valueOf(accountId); + long rawContactId = 0; + final Cursor cursor = db.query(RawContactsBackupQuery.TABLE, + RawContactsBackupQuery.COLUMNS, RawContactsBackupQuery.SELECTION, + mSelectionArgs2, null, null, null); + try { + if (cursor.moveToFirst()) { + rawContactId = cursor.getLong(RawContactsBackupQuery.RAW_CONTACT_ID); + } + } finally { + cursor.close(); + } + return rawContactId; + } + + interface DataHashQuery { + String TABLE = Tables.DATA; + String[] COLUMNS = new String[] { + Data._ID, + }; + int DATA_ID = 0; + String SELECTION = Data.HASH_ID + "=?"; + } + + /** + * Fetch a list of dataId related to the given hashId. + * Return empty list if there's no such data. + */ + private ArrayList<Long> queryDataId(SQLiteDatabase db, String hashId) { + if (TextUtils.isEmpty(hashId)) { + return new ArrayList<>(); + } + mSelectionArgs1[0] = hashId; + ArrayList<Long> result = new ArrayList<>(); + long dataId = 0; + final Cursor c = db.query(DataHashQuery.TABLE, DataHashQuery.COLUMNS, + DataHashQuery.SELECTION, mSelectionArgs1, null, null, null); + try { + while (c.moveToNext()) { + dataId = c.getLong(DataHashQuery.DATA_ID); + result.add(dataId); + } + } finally { + c.close(); + } + return result; + } + + private long searchRawContactIdForRawContactInfo(SQLiteDatabase db, + RawContactInfo rawContactInfo) { + if (rawContactInfo == null) { + return 0; + } + final String backupId = rawContactInfo.mBackupId; + final String accountType = rawContactInfo.mAccountType; + final String accountName = rawContactInfo.mAccountName; + final String dataSet = rawContactInfo.mDataSet; + ContentValues values = new ContentValues(); + values.put(AccountsColumns.ACCOUNT_TYPE, accountType); + values.put(AccountsColumns.ACCOUNT_NAME, accountName); + if (dataSet != null) { + values.put(AccountsColumns.DATA_SET, dataSet); + } + + final long accountId = replaceAccountInfoByAccountId(RawContacts.CONTENT_URI, values); + final long rawContactId = queryRawContactId(db, backupId, accountId); + return rawContactId; + } + + /** + * Update RawContact, Data, DataUsageStats, AggregationException tables from MetadataEntry. + */ + @NeededForTesting + void updateFromMetaDataEntry(SQLiteDatabase db, MetadataEntry metadataEntry) { + final RawContactInfo rawContactInfo = metadataEntry.mRawContactInfo; + final long rawContactId = searchRawContactIdForRawContactInfo(db, rawContactInfo); + if (rawContactId == 0) { + return; + } + + ContentValues rawContactValues = new ContentValues(); + rawContactValues.put(RawContacts.SEND_TO_VOICEMAIL, metadataEntry.mSendToVoicemail); + rawContactValues.put(RawContacts.STARRED, metadataEntry.mStarred); + rawContactValues.put(RawContacts.PINNED, metadataEntry.mPinned); + updateRawContact(db, rawContactId, rawContactValues, true); + + // Update Data and DataUsageStats table. + for (int i = 0; i < metadataEntry.mFieldDatas.size(); i++) { + final FieldData fieldData = metadataEntry.mFieldDatas.get(i); + final String dataHashId = fieldData.mDataHashId; + final ArrayList<Long> dataIds = queryDataId(db, dataHashId); + + for (long dataId : dataIds) { + // Update is_primary and is_super_primary. + ContentValues dataValues = new ContentValues(); + dataValues.put(Data.IS_PRIMARY, fieldData.mIsPrimary); + dataValues.put(Data.IS_SUPER_PRIMARY, fieldData.mIsSuperPrimary); + updateData(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), + dataValues, null, null, true); + + // Update UsageStats. + for (int j = 0; j < fieldData.mUsageStatsList.size(); j++) { + final UsageStats usageStats = fieldData.mUsageStatsList.get(j); + final String usageType = usageStats.mUsageType; + final int typeInt = getDataUsageFeedbackType(usageType.toLowerCase(), null); + final long lastTimeUsed = usageStats.mLastTimeUsed; + final int timesUsed = usageStats.mTimesUsed; + ContentValues usageStatsValues = new ContentValues(); + usageStatsValues.put(DataUsageStatColumns.DATA_ID, dataId); + usageStatsValues.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt); + usageStatsValues.put(DataUsageStatColumns.LAST_TIME_USED, lastTimeUsed); + usageStatsValues.put(DataUsageStatColumns.TIMES_USED, timesUsed); + updateDataUsageStats(db, usageStatsValues); + } + } + } + + // Update AggregationException table. + for (int i = 0; i < metadataEntry.mAggregationDatas.size(); i++) { + final AggregationData aggregationData = metadataEntry.mAggregationDatas.get(i); + final int typeInt = getAggregationType(aggregationData.mType, null); + final RawContactInfo aggregationContact1 = aggregationData.mRawContactInfo1; + final RawContactInfo aggregationContact2 = aggregationData.mRawContactInfo2; + final long rawContactId1 = searchRawContactIdForRawContactInfo(db, aggregationContact1); + final long rawContactId2 = searchRawContactIdForRawContactInfo(db, aggregationContact2); + if (rawContactId1 == 0 || rawContactId2 == 0) { + continue; + } + ContentValues values = new ContentValues(); + values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1); + values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2); + values.put(AggregationExceptions.TYPE, typeInt); + updateAggregationException(db, values); + } + } + /** return serialized version of {@code accounts} */ @VisibleForTesting static String accountsToString(Set<Account> accounts) { @@ -9061,6 +9222,52 @@ public class ContactsProvider2 extends AbstractContactsProvider } /** + * Update {@link Tables#DATA_USAGE_STAT}. + * Update or insert usageType, lastTimeUsed, and timesUsed for specific dataId. + */ + private void updateDataUsageStats(SQLiteDatabase db, ContentValues values) { + final String dataId = values.getAsString(DataUsageStatColumns.DATA_ID); + final String type = values.getAsString(DataUsageStatColumns.USAGE_TYPE_INT); + final String lastTimeUsed = values.getAsString(DataUsageStatColumns.LAST_TIME_USED); + final String timesUsed = values.getAsString(DataUsageStatColumns.TIMES_USED); + + mSelectionArgs2[0] = dataId; + mSelectionArgs2[1] = type; + final Cursor cursor = db.query(DataUsageStatQuery.TABLE, + DataUsageStatQuery.COLUMNS, DataUsageStatQuery.SELECTION, + mSelectionArgs2, null, null, null); + + try { + if (cursor.moveToFirst()) { + final long id = cursor.getLong(DataUsageStatQuery.ID); + + mSelectionArgs3[0] = lastTimeUsed; + mSelectionArgs3[1] = timesUsed; + mSelectionArgs3[2] = String.valueOf(id); + db.execSQL("UPDATE " + Tables.DATA_USAGE_STAT + + " SET " + DataUsageStatColumns.LAST_TIME_USED + "=?" + + "," + DataUsageStatColumns.TIMES_USED + "=?" + + " WHERE " + DataUsageStatColumns._ID + "=?", + mSelectionArgs3); + } else { + mSelectionArgs4[0] = dataId; + mSelectionArgs4[1] = type; + mSelectionArgs4[2] = timesUsed; + mSelectionArgs4[3] = lastTimeUsed; + db.execSQL("INSERT INTO " + Tables.DATA_USAGE_STAT + + "(" + DataUsageStatColumns.DATA_ID + + "," + DataUsageStatColumns.USAGE_TYPE_INT + + "," + DataUsageStatColumns.TIMES_USED + + "," + DataUsageStatColumns.LAST_TIME_USED + + ") VALUES (?,?,?,?)", + mSelectionArgs4); + } + } finally { + cursor.close(); + } + } + + /** * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.) * associated with a primary account. The primary account should be supplied from applications * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and @@ -9234,6 +9441,22 @@ public class ContactsProvider2 extends AbstractContactsProvider throw new IllegalArgumentException("Invalid usage type " + type); } + private static final int getAggregationType(String type, Integer defaultType) { + if ("TOGETHER".equalsIgnoreCase(type)) { + return AggregationExceptions.TYPE_KEEP_TOGETHER; // 1 + } + if ("SEPARATE".equalsIgnoreCase(type)) { + return AggregationExceptions.TYPE_KEEP_SEPARATE; // 2 + } + if ("UNSET".equalsIgnoreCase(type)) { + return AggregationExceptions.TYPE_AUTOMATIC; // 0 + } + if (defaultType != null) { + return defaultType; + } + throw new IllegalArgumentException("Invalid aggregation type " + type); + } + /** Use only for debug logging */ @Override public String toString() { diff --git a/src/com/android/providers/contacts/MetadataEntryParser.java b/src/com/android/providers/contacts/MetadataEntryParser.java index a276cae..b90f938 100644 --- a/src/com/android/providers/contacts/MetadataEntryParser.java +++ b/src/com/android/providers/contacts/MetadataEntryParser.java @@ -31,8 +31,15 @@ public class MetadataEntryParser { private final static String UNIQUE_CONTACT_ID = "unique_contact_id"; private final static String ACCOUNT_TYPE = "account_type"; private final static String CUSTOM_ACCOUNT_TYPE = "custom_account_type"; + private final static String ENUM_VALUE_FOR_GOOGLE_ACCOUNT = "GOOGLE_ACCOUNT"; + private final static String GOOGLE_ACCOUNT_TYPE = "com.google"; private final static String ENUM_VALUE_FOR_CUSTOM_ACCOUNT = "CUSTOM_ACCOUNT"; private final static String ACCOUNT_NAME = "account_name"; + private final static String DATA_SET = "data_set"; + private final static String ENUM_FOR_PLUS_DATA_SET = "GOOGLE_PLUS"; + private final static String ENUM_FOR_CUSTOM_DATA_SET = "CUSTOM"; + private final static String PLUS_DATA_SET_TYPE = "plus"; + private final static String CUSTOM_DATA_SET = "custom_data_set"; private final static String CONTACT_ID = "contact_id"; private final static String CONTACT_PREFS = "contact_prefs"; private final static String SEND_TO_VOICEMAIL = "send_to_voicemail"; @@ -67,15 +74,15 @@ public class MetadataEntryParser { @NeededForTesting public static class FieldData { - final long mFieldDataId; + final String mDataHashId; final boolean mIsPrimary; final boolean mIsSuperPrimary; final ArrayList<UsageStats> mUsageStatsList; @NeededForTesting - public FieldData(long fieldDataId, boolean isPrimary, boolean isSuperPrimary, + public FieldData(String dataHashId, boolean isPrimary, boolean isSuperPrimary, ArrayList<UsageStats> usageStatsList) { - this.mFieldDataId = fieldDataId; + this.mDataHashId = dataHashId; this.mIsPrimary = isPrimary; this.mIsSuperPrimary = isSuperPrimary; this.mUsageStatsList = usageStatsList; @@ -83,24 +90,40 @@ public class MetadataEntryParser { } @NeededForTesting + public static class RawContactInfo { + final String mBackupId; + final String mAccountType; + final String mAccountName; + final String mDataSet; + + @NeededForTesting + public RawContactInfo(String backupId, String accountType, String accountName, + String dataSet) { + this.mBackupId = backupId; + this.mAccountType = accountType; + this.mAccountName = accountName; + mDataSet = dataSet; + } + } + + @NeededForTesting public static class AggregationData { - final long mRawContactId1; - final long mRawContactId2; + final RawContactInfo mRawContactInfo1; + final RawContactInfo mRawContactInfo2; final String mType; @NeededForTesting - public AggregationData(long rawContactId1, long rawContactId2, String type) { - this.mRawContactId1 = rawContactId1; - this.mRawContactId2 = rawContactId2; + public AggregationData(RawContactInfo rawContactInfo1, RawContactInfo rawContactInfo2, + String type) { + this.mRawContactInfo1 = rawContactInfo1; + this.mRawContactInfo2 = rawContactInfo2; this.mType = type; } } @NeededForTesting public static class MetadataEntry { - final long mRawContactId; - final String mAccountType; - final String mAccountName; + final RawContactInfo mRawContactInfo; final int mSendToVoicemail; final int mStarred; final int mPinned; @@ -108,13 +131,11 @@ public class MetadataEntryParser { final ArrayList<AggregationData> mAggregationDatas; @NeededForTesting - public MetadataEntry(long rawContactId, String accountType, String accountName, + public MetadataEntry(RawContactInfo rawContactInfo, int sendToVoicemail, int starred, int pinned, - ArrayList<FieldData> fieldDatas, - ArrayList<AggregationData> aggregationDatas) { - this.mRawContactId = rawContactId; - this.mAccountType = accountType; - this.mAccountName = accountName; + ArrayList<FieldData> fieldDatas, + ArrayList<AggregationData> aggregationDatas) { + this.mRawContactInfo = rawContactInfo; this.mSendToVoicemail = sendToVoicemail; this.mStarred = starred; this.mPinned = pinned; @@ -132,18 +153,8 @@ public class MetadataEntryParser { try { final JSONObject root = new JSONObject(inputData); // Parse to get rawContactId and account info. - final JSONObject uniqueContact = root.getJSONObject(UNIQUE_CONTACT_ID); - final String rawContactId = uniqueContact.getString(CONTACT_ID); - String accountType = uniqueContact.getString(ACCOUNT_TYPE); - if (ENUM_VALUE_FOR_CUSTOM_ACCOUNT.equals(accountType)) { - accountType = uniqueContact.getString(CUSTOM_ACCOUNT_TYPE); - } - final String accountName = uniqueContact.getString(ACCOUNT_NAME); - if (TextUtils.isEmpty(rawContactId) || TextUtils.isEmpty(accountType) - || TextUtils.isEmpty(accountName)) { - throw new IllegalArgumentException( - "contact_id, account_type, account_name cannot be empty."); - } + final JSONObject uniqueContactJSON = root.getJSONObject(UNIQUE_CONTACT_ID); + final RawContactInfo rawContactInfo = parseUniqueContact(uniqueContactJSON); // Parse contactPrefs to get sendToVoicemail, starred, pinned. final JSONObject contactPrefs = root.getJSONObject(CONTACT_PREFS); @@ -165,16 +176,16 @@ public class MetadataEntryParser { "There should be two contacts for each aggregation."); } final JSONObject rawContact1 = contacts.getJSONObject(0); - final long rawContactId1 = rawContact1.getLong(CONTACT_ID); + final RawContactInfo aggregationContact1 = parseUniqueContact(rawContact1); final JSONObject rawContact2 = contacts.getJSONObject(1); - final long rawContactId2 = rawContact2.getLong(CONTACT_ID); + final RawContactInfo aggregationContact2 = parseUniqueContact(rawContact2); final String type = aggregationData.getString(TYPE); if (TextUtils.isEmpty(type)) { throw new IllegalArgumentException("Aggregation type cannot be empty."); } final AggregationData aggregation = new AggregationData( - rawContactId1, rawContactId2, type); + aggregationContact1, aggregationContact2, type); aggregationsList.add(aggregation); } } @@ -186,7 +197,10 @@ public class MetadataEntryParser { for (int i = 0; i < fieldDatas.length(); i++) { final JSONObject fieldData = fieldDatas.getJSONObject(i); - final long fieldDataId = fieldData.getLong(FIELD_DATA_ID); + final String dataHashId = fieldData.getString(FIELD_DATA_ID); + if (TextUtils.isEmpty(dataHashId)) { + throw new IllegalArgumentException("Field data hash id cannot be empty."); + } final JSONObject fieldDataPrefs = fieldData.getJSONObject(FIELD_DATA_PREFS); final boolean isPrimary = fieldDataPrefs.getBoolean(IS_PRIMARY); final boolean isSuperPrimary = fieldDataPrefs.getBoolean(IS_SUPER_PRIMARY); @@ -209,17 +223,52 @@ public class MetadataEntryParser { } } - final FieldData fieldDataParse = new FieldData(fieldDataId, isPrimary, + final FieldData fieldDataParse = new FieldData(dataHashId, isPrimary, isSuperPrimary, usageStatsList); fieldDatasList.add(fieldDataParse); } } - final MetadataEntry metaDataEntry = new MetadataEntry(Long.parseLong(rawContactId), - accountType, accountName, sendToVoicemail ? 1 : 0, starred ? 1 : 0, pinned, + final MetadataEntry metaDataEntry = new MetadataEntry(rawContactInfo, + sendToVoicemail ? 1 : 0, starred ? 1 : 0, pinned, fieldDatasList, aggregationsList); return metaDataEntry; } catch (JSONException e) { throw new IllegalArgumentException("JSON Exception.", e); } } + + private static RawContactInfo parseUniqueContact(JSONObject uniqueContactJSON) { + try { + final String backupId = uniqueContactJSON.getString(CONTACT_ID); + final String accountName = uniqueContactJSON.getString(ACCOUNT_NAME); + String accountType = uniqueContactJSON.getString(ACCOUNT_TYPE); + if (ENUM_VALUE_FOR_GOOGLE_ACCOUNT.equals(accountType)) { + accountType = GOOGLE_ACCOUNT_TYPE; + } else if (ENUM_VALUE_FOR_CUSTOM_ACCOUNT.equals(accountType)) { + accountType = uniqueContactJSON.getString(CUSTOM_ACCOUNT_TYPE); + } else { + throw new IllegalArgumentException("Unknown account type."); + } + + String dataSet = null; + switch (uniqueContactJSON.getString(DATA_SET)) { + case ENUM_FOR_PLUS_DATA_SET: + dataSet = PLUS_DATA_SET_TYPE; + break; + case ENUM_FOR_CUSTOM_DATA_SET: + dataSet = uniqueContactJSON.getString(CUSTOM_DATA_SET); + break; + } + if (TextUtils.isEmpty(backupId) || TextUtils.isEmpty(accountType) + || TextUtils.isEmpty(accountName)) { + throw new IllegalArgumentException( + "Contact backup id, account type, account name cannot be empty."); + } + final RawContactInfo rawContactInfo = new RawContactInfo( + backupId, accountType, accountName, dataSet); + return rawContactInfo; + } catch (JSONException e) { + throw new IllegalArgumentException("JSON Exception.", e); + } + } } diff --git a/tests/assets/test1/testFileDeviceContactMetadataJSON.txt b/tests/assets/test1/testFileDeviceContactMetadataJSON.txt index c9494d8..65e624d 100644 --- a/tests/assets/test1/testFileDeviceContactMetadataJSON.txt +++ b/tests/assets/test1/testFileDeviceContactMetadataJSON.txt @@ -3,7 +3,8 @@ "account_type": "CUSTOM_ACCOUNT", "custom_account_type": "facebook", "account_name": "android-test", - "contact_id": "1111111" + "contact_id": "1111111", + "data_set": "FOCUS" }, "contact_prefs": { "send_to_voicemail": true, @@ -15,10 +16,17 @@ "type": "TOGETHER", "contact_ids": [ { - "contact_id": "2222222" + "account_type": "GOOGLE_ACCOUNT", + "account_name": "android-test2", + "contact_id": "2222222", + "data_set": "GOOGLE_PLUS" }, { - "contact_id": "3333333" + "account_type": "GOOGLE_ACCOUNT", + "account_name": "android-test3", + "contact_id": "3333333", + "data_set": "CUSTOM", + "custom_data_set": "custom type" } ] } diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java index 0e3c68b..18279ac 100644 --- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java @@ -84,6 +84,11 @@ import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties; import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns; import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns; import com.android.providers.contacts.ContactsDatabaseHelper.Tables; +import com.android.providers.contacts.MetadataEntryParser.AggregationData; +import com.android.providers.contacts.MetadataEntryParser.FieldData; +import com.android.providers.contacts.MetadataEntryParser.MetadataEntry; +import com.android.providers.contacts.MetadataEntryParser.RawContactInfo; +import com.android.providers.contacts.MetadataEntryParser.UsageStats; import com.android.providers.contacts.testutil.CommonDatabaseUtils; import com.android.providers.contacts.testutil.ContactUtil; import com.android.providers.contacts.testutil.DataUtil; @@ -2510,6 +2515,110 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { assertStoredValuesOrderly(filterUri1, new ContentValues[] { v2, v1, v4, v3 }); } + public void testUpdateFromMetadataEntry() { + String accountType1 = "accountType1"; + String accountName1 = "accountName1"; + String dataSet1 = "plus"; + Account account1 = new Account(accountName1, accountType1); + long rawContactId = RawContactUtil.createRawContactWithName(mResolver, account1); + Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); + // Add backup_id for the raw contact. + String backupId = "backupId100001"; + ContentValues values = new ContentValues(); + values.put(RawContacts.BACKUP_ID, backupId); + assertEquals(1, mResolver.update(rawContactUri, values, null, null)); + + String emailAddress = "address@email.com"; + Uri dataUri = insertEmail(rawContactId, emailAddress); + String hashId = "hashId100002"; + ContentValues dataValues = new ContentValues(); + dataValues.put(Data.HASH_ID, hashId); + assertEquals(1, mResolver.update(dataUri, dataValues, null, null)); + + // Another data that should not be updated. + String phoneNumber = "111-111-1111"; + Uri dataUri2 = insertPhoneNumber(rawContactId, phoneNumber); + String hashId2 = "hashId100004"; + ContentValues dataValues2 = new ContentValues(); + dataValues.put(Data.HASH_ID, hashId2); + mResolver.update(dataUri2, dataValues2, null, null); + + String accountType2 = "accountType2"; + String accountName2 = "accountName2"; + Account account2 = new Account(accountName2, accountType2); + long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, account2); + Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2); + String backupId2 = "backupId100003"; + ContentValues values2 = new ContentValues(); + values2.put(RawContacts.BACKUP_ID, backupId2); + assertEquals(1, mResolver.update(rawContactUri2, values2, null, null)); + + String usageTypeString = "CALL"; + int lastTimeUsed = 1111111; + int timesUsed = 5; + String aggregationTypeString = "SEPARATE"; + int aggregationType = AggregationExceptions.TYPE_KEEP_SEPARATE; + + RawContactInfo rawContactInfo = new RawContactInfo( + backupId, accountType1, accountName1, null); + UsageStats usageStats = new UsageStats(usageTypeString, lastTimeUsed, timesUsed); + ArrayList<UsageStats> usageStatsList = new ArrayList<>(); + usageStatsList.add(usageStats); + FieldData fieldData = new FieldData(hashId, true, true, usageStatsList); + ArrayList<FieldData> fieldDataList = new ArrayList<>(); + fieldDataList.add(fieldData); + ArrayList<AggregationData> aggregationDataList = new ArrayList<>(); + MetadataEntry metadataEntry = new MetadataEntry(rawContactInfo, + 1, 1, 1, fieldDataList, aggregationDataList); + + ContactsProvider2 provider = (ContactsProvider2) getProvider(); + final ContactsDatabaseHelper helper = + ((ContactsDatabaseHelper) provider.getDatabaseHelper()); + SQLiteDatabase db = helper.getWritableDatabase(); + + // Before updating tables from MetadataEntry. + assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, accountType1); + assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, accountName1); + assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "0"); + assertStoredValue(rawContactUri, RawContacts.STARRED, "0"); + assertStoredValue(rawContactUri, RawContacts.PINNED, "0"); + assertStoredValue(dataUri, Data.IS_PRIMARY, 0); + assertStoredValue(dataUri, Data.IS_SUPER_PRIMARY, 0); + + // Update tables without aggregation first, since aggregator will affect pinned value. + provider.updateFromMetaDataEntry(db, metadataEntry); + + // After updating tables from MetadataEntry. + assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, accountType1); + assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, accountName1); + assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "1"); + assertStoredValue(rawContactUri, RawContacts.STARRED, "1"); + assertStoredValue(rawContactUri, RawContacts.PINNED, "1"); + assertStoredValue(dataUri, Data.IS_PRIMARY, 1); + assertStoredValue(dataUri, Data.IS_SUPER_PRIMARY, 1); + final Uri dataUriWithUsageType = Data.CONTENT_URI.buildUpon().appendQueryParameter( + DataUsageFeedback.USAGE_TYPE, usageTypeString).build(); + assertDataUsageCursorContains(dataUriWithUsageType, emailAddress, timesUsed, lastTimeUsed); + + // Update AggregationException table. + RawContactInfo aggregationContact = new RawContactInfo( + backupId2, accountType2, accountName2, null); + AggregationData aggregationData = new AggregationData( + rawContactInfo, aggregationContact, aggregationTypeString); + aggregationDataList.add(aggregationData); + metadataEntry = new MetadataEntry(rawContactInfo, + 1, 1, 1, fieldDataList, aggregationDataList); + provider.updateFromMetaDataEntry(db, metadataEntry); + + // Check if AggregationException table is updated. + assertStoredValue(AggregationExceptions.CONTENT_URI, AggregationExceptions.RAW_CONTACT_ID1, + rawContactId); + assertStoredValue(AggregationExceptions.CONTENT_URI, AggregationExceptions.RAW_CONTACT_ID2, + rawContactId2); + assertStoredValue(AggregationExceptions.CONTENT_URI, AggregationExceptions.TYPE, + aggregationType); + } + public void testPostalsQuery() { long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Alice", "Nextore"); Uri dataUri = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View"); diff --git a/tests/src/com/android/providers/contacts/MetadataEntryParserTest.java b/tests/src/com/android/providers/contacts/MetadataEntryParserTest.java index 94ca074..be4df57 100644 --- a/tests/src/com/android/providers/contacts/MetadataEntryParserTest.java +++ b/tests/src/com/android/providers/contacts/MetadataEntryParserTest.java @@ -22,6 +22,7 @@ import android.test.suitebuilder.annotation.SmallTest; import com.android.providers.contacts.MetadataEntryParser.AggregationData; import com.android.providers.contacts.MetadataEntryParser.FieldData; import com.android.providers.contacts.MetadataEntryParser.MetadataEntry; +import com.android.providers.contacts.MetadataEntryParser.RawContactInfo; import com.android.providers.contacts.MetadataEntryParser.UsageStats; import org.json.JSONException; @@ -51,28 +52,41 @@ public class MetadataEntryParserTest extends AndroidTestCase { } public void testParseDataToMetadataEntry() throws IOException { - long rawContactId = 1111111; + String contactBackupId = "1111111"; String accountType = "facebook"; String accountName = "android-test"; + String dataSet = null; int sendToVoicemail = 1; int starred = 0; int pinned = 2; - long fieldDataId1 = 1001; + String dataHashId1 = "1001"; String usageType1_1 = "CALL"; long lastTimeUsed1_1 = 10000001; int timesUsed1_1 = 10; String usageType1_2 = "SHORT_TEXT"; long lastTimeUsed1_2 = 20000002; int timesUsed1_2 = 20; - long fieldDataId2 = 1002; + String dataHashId2 = "1002"; String usageType2 = "LONG_TEXT"; long lastTimeUsed2 = 30000003; int timesUsed2 = 30; - long aggregationContact1 = 2222222; - long aggregationContact2 = 3333333; + String aggregationContactBackupId1 = "2222222"; + String aggregationAccountType1 = "com.google"; + String aggregationAccountName1 = "android-test2"; + String aggregationDataSet1 = "plus"; + String aggregationContactBackupId2 = "3333333"; + String aggregationAccountType2 = "com.google"; + String aggregationAccountName2 = "android-test3"; + String aggregationDataSet2 = "custom type"; String type = "TOGETHER"; String inputFile = "test1/testFileDeviceContactMetadataJSON.txt"; + RawContactInfo rawContactInfo = new RawContactInfo( + contactBackupId, accountType, accountName, dataSet); + RawContactInfo aggregationContact1 = new RawContactInfo(aggregationContactBackupId1, + aggregationAccountType1, aggregationAccountName1, aggregationDataSet1); + RawContactInfo aggregationContact2 = new RawContactInfo(aggregationContactBackupId2, + aggregationAccountType2, aggregationAccountName2, aggregationDataSet2); AggregationData aggregationData = new AggregationData( aggregationContact1, aggregationContact2, type); ArrayList<AggregationData> aggregationDataList = new ArrayList<>(); @@ -85,17 +99,17 @@ public class MetadataEntryParserTest extends AndroidTestCase { ArrayList<UsageStats> usageStats1List = new ArrayList<>(); usageStats1List.add(usageStats1_1); usageStats1List.add(usageStats1_2); - FieldData fieldData1 = new FieldData(fieldDataId1, true, true, usageStats1List); + FieldData fieldData1 = new FieldData(dataHashId1, true, true, usageStats1List); ArrayList<UsageStats> usageStats2List = new ArrayList<>(); usageStats2List.add(usageStats2); - FieldData fieldData2 = new FieldData(fieldDataId2, false, false, usageStats2List); + FieldData fieldData2 = new FieldData(dataHashId2, false, false, usageStats2List); ArrayList<FieldData> fieldDataList = new ArrayList<>(); fieldDataList.add(fieldData1); fieldDataList.add(fieldData2); - MetadataEntry expectedResult = new MetadataEntry(rawContactId, accountType, accountName, + MetadataEntry expectedResult = new MetadataEntry(rawContactInfo, sendToVoicemail, starred, pinned, fieldDataList, aggregationDataList); String inputJson = readAssetAsString(inputFile); @@ -228,9 +242,7 @@ public class MetadataEntryParserTest extends AndroidTestCase { } private void assertMetaDataEntry(MetadataEntry entry1, MetadataEntry entry2) { - assertEquals(entry1.mRawContactId, entry2.mRawContactId); - assertEquals(entry1.mAccountType, entry2.mAccountType); - assertEquals(entry1.mAccountName, entry2.mAccountName); + assertRawContactInfoEquals(entry1.mRawContactInfo, entry2.mRawContactInfo); assertEquals(entry1.mSendToVoicemail, entry2.mSendToVoicemail); assertEquals(entry1.mStarred, entry2.mStarred); assertEquals(entry1.mPinned, entry2.mPinned); @@ -238,6 +250,13 @@ public class MetadataEntryParserTest extends AndroidTestCase { assertFieldDataListEquals(entry1.mFieldDatas, entry2.mFieldDatas); } + private void assertRawContactInfoEquals(RawContactInfo contact1, RawContactInfo contact2) { + assertEquals(contact1.mBackupId, contact2.mBackupId); + assertEquals(contact1.mAccountType, contact2.mAccountType); + assertEquals(contact1.mAccountName, contact2.mAccountName); + assertEquals(contact1.mDataSet, contact2.mDataSet); + } + private void assertAggregationDataListEquals(ArrayList<AggregationData> aggregationList1, ArrayList<AggregationData> aggregationList2) { assertEquals(aggregationList1.size(), aggregationList2.size()); @@ -248,8 +267,10 @@ public class MetadataEntryParserTest extends AndroidTestCase { private void assertAggregationDataEquals(AggregationData aggregationData1, AggregationData aggregationData2) { - assertEquals(aggregationData1.mRawContactId1, aggregationData2.mRawContactId1); - assertEquals(aggregationData1.mRawContactId2, aggregationData2.mRawContactId2); + assertRawContactInfoEquals(aggregationData1.mRawContactInfo1, + aggregationData2.mRawContactInfo1); + assertRawContactInfoEquals(aggregationData1.mRawContactInfo2, + aggregationData2.mRawContactInfo2); assertEquals(aggregationData1.mType, aggregationData2.mType); } @@ -262,7 +283,7 @@ public class MetadataEntryParserTest extends AndroidTestCase { } private void assertFieldDataEquals(FieldData fieldData1, FieldData fieldData2) { - assertEquals(fieldData1.mFieldDataId, fieldData2.mFieldDataId); + assertEquals(fieldData1.mDataHashId, fieldData2.mDataHashId); assertEquals(fieldData1.mIsPrimary, fieldData2.mIsPrimary); assertEquals(fieldData1.mIsSuperPrimary, fieldData2.mIsSuperPrimary); assertUsageStatsListEquals(fieldData1.mUsageStatsList, fieldData2.mUsageStatsList); |