summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/com/android/providers/contacts/ContactsProvider2.java223
-rw-r--r--src/com/android/providers/contacts/MetadataEntryParser.java121
-rw-r--r--tests/assets/test1/testFileDeviceContactMetadataJSON.txt14
-rw-r--r--tests/src/com/android/providers/contacts/ContactsProvider2Test.java109
-rw-r--r--tests/src/com/android/providers/contacts/MetadataEntryParserTest.java49
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 47dbf2e7..03ef0370 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -151,6 +151,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;
@@ -4664,6 +4669,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) {
@@ -9039,6 +9200,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
@@ -9209,6 +9416,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 a276cae2..b90f9381 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 c9494d8c..65e624d1 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 6a175ede..fe164ab7 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 94ca074a..be4df57c 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);