[automerger skipped] Import translations. DO NOT MERGE am: 2ebfe4131e -s ours
am skip reason: subject contains skip directive
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/providers/ContactsProvider/+/12077358
Change-Id: Ia763141bf7a6cfdb52e50e7bdb722992daf9e519
diff --git a/Android.bp b/Android.bp
index b6bddce..8991628 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6,8 +6,7 @@
"src/com/android/providers/contacts/EventLogTags.logtags",
],
libs: [
- "ext",
- "telephony-common",
+ "ext"
],
static_libs: [
"android-common",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2da56fc..9542e6d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -11,6 +11,7 @@
<uses-permission android:name="android.permission.PROCESS_PHONE_ACCOUNT_REGISTRATION" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.SEND_CALL_LOG_CHANGE" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
@@ -25,8 +26,8 @@
<application android:process="android.process.acore"
android:label="@string/app_label"
- android:icon="@drawable/app_icon"
android:allowBackup="false"
+ android:forceQueryable="true"
android:usesCleartextTraffic="false">
<provider android:name="ContactsProvider2"
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..c66ff24
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit": [
+ {
+ "name": "ContactsProviderTests"
+ },
+ {
+ "name": "CtsProviderTestCases",
+ "options": [
+ {
+ "include-filter": "android.provider.cts.contacts."
+ }
+ ]
+ }
+ ]
+}
diff --git a/res/drawable-hdpi/app_icon.png b/res/drawable-hdpi/app_icon.png
deleted file mode 100644
index 64eff00..0000000
--- a/res/drawable-hdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/app_icon.png b/res/drawable-mdpi/app_icon.png
deleted file mode 100644
index b4ee821..0000000
--- a/res/drawable-mdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/app_icon.png b/res/drawable-xhdpi/app_icon.png
deleted file mode 100644
index 6feeadf..0000000
--- a/res/drawable-xhdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/app_icon.png b/res/drawable-xxhdpi/app_icon.png
deleted file mode 100644
index 01a3fde..0000000
--- a/res/drawable-xxhdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/app_icon.png b/res/drawable-xxxhdpi/app_icon.png
deleted file mode 100644
index 328e067..0000000
--- a/res/drawable-xxxhdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java
index ff3e65c..ec1d40e 100644
--- a/src/com/android/providers/contacts/CallLogProvider.java
+++ b/src/com/android/providers/contacts/CallLogProvider.java
@@ -183,6 +183,7 @@
private CallLogDatabaseHelper mDbHelper;
private DatabaseUtils.InsertHelper mCallsInserter;
private boolean mUseStrictPhoneNumberComparation;
+ private int mMinMatch;
private VoicemailPermissions mVoicemailPermissions;
private CallLogInsertionHelper mCallLogInsertionHelper;
@@ -214,6 +215,9 @@
mUseStrictPhoneNumberComparation =
context.getResources().getBoolean(
com.android.internal.R.bool.config_use_strict_phone_number_comparation);
+ mMinMatch =
+ context.getResources().getInteger(
+ com.android.internal.R.integer.config_phonenumber_compare_min_match);
mVoicemailPermissions = new VoicemailPermissions(context);
mCallLogInsertionHelper = createCallLogInsertionHelper(context);
@@ -239,6 +243,16 @@
return DefaultCallLogInsertionHelper.getInstance(context);
}
+ @VisibleForTesting
+ public void setMinMatchForTest(int minMatch) {
+ mMinMatch = minMatch;
+ }
+
+ @VisibleForTesting
+ public int getMinMatchForTest() {
+ return mMinMatch;
+ }
+
protected CallLogDatabaseHelper getDatabaseHelper(final Context context) {
return CallLogDatabaseHelper.getInstance(context);
}
@@ -299,6 +313,7 @@
Log.v(TAG, "query: uri=" + uri + " projection=" + Arrays.toString(projection) +
" selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) +
" order=[" + sortOrder + "] CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid() +
" User=" + UserUtils.getCurrentUserHandle(getContext()));
}
@@ -331,7 +346,8 @@
if (!TextUtils.isEmpty(phoneNumber)) {
qb.appendWhere("PHONE_NUMBERS_EQUAL(number, ");
qb.appendWhereEscapeString(phoneNumber);
- qb.appendWhere(mUseStrictPhoneNumberComparation ? ", 1)" : ", 0)");
+ qb.appendWhere(mUseStrictPhoneNumberComparation ? ", 1)"
+ : ", 0, " + mMinMatch + ")");
} else {
qb.appendWhere(Calls.NUMBER_PRESENTATION + "!="
+ Calls.PRESENTATION_ALLOWED);
@@ -465,7 +481,8 @@
private Uri insertInternal(Uri uri, ContentValues values) {
if (VERBOSE_LOGGING) {
Log.v(TAG, "insert: uri=" + uri + " values=[" + values + "]" +
- " CPID=" + Binder.getCallingPid());
+ " CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid());
}
waitForAccess(mReadAccessLatch);
checkForSupportedColumns(sCallsProjectionMap, values);
@@ -498,6 +515,7 @@
Log.v(TAG, "update: uri=" + uri +
" selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) +
" values=[" + values + "] CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid() +
" User=" + UserUtils.getCurrentUserHandle(getContext()));
}
waitForAccess(mReadAccessLatch);
@@ -534,6 +552,7 @@
Log.v(TAG, "delete: uri=" + uri +
" selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) +
" CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid() +
" User=" + UserUtils.getCurrentUserHandle(getContext()));
}
waitForAccess(mReadAccessLatch);
@@ -728,9 +747,8 @@
mDbHelper.getWritableDatabase().execSQL(
UNHIDE_BY_PHONE_ACCOUNT_QUERY, handleArgs);
} else {
- TelecomManager tm = TelecomManager.from(getContext());
+ TelecomManager tm = getContext().getSystemService(TelecomManager.class);
if (tm != null) {
-
PhoneAccount account = tm.getPhoneAccount(handle);
if (account != null && account.getAddress() != null) {
// We did not find any items for the specific phone account, so run the
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index 4c521f4..2e5cdac 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -982,6 +982,7 @@
private boolean mUseStrictPhoneNumberComparisonBase;
private boolean mUseStrictPhoneNumberComparisonForRussia;
private boolean mUseStrictPhoneNumberComparisonForKazakhstan;
+ private int mMinMatch;
private String[] mSelectionArgs1 = new String[1];
private NameSplitter.Name mName = new NameSplitter.Name();
@@ -1071,6 +1072,9 @@
} else {
mUseStrictPhoneNumberComparison = mUseStrictPhoneNumberComparisonBase;
}
+
+ mMinMatch = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_phonenumber_compare_min_match);
}
private boolean getConfig(String configKey, int defaultResId) {
@@ -3217,8 +3221,7 @@
// ON_BOOT_COMPLETE instead of PRE_BOOT_COMPLETE.
SubscriptionManager sm = SubscriptionManager.from(mContext);
if (sm != null) {
- Log.i(TAG, "count: " + sm.getAllSubscriptionInfoCount());
- for (SubscriptionInfo info : sm.getAllSubscriptionInfoList()) {
+ for (SubscriptionInfo info : sm.getActiveSubscriptionInfoList()) {
String iccId = info.getIccId();
int subId = info.getSubscriptionId();
if (!TextUtils.isEmpty(iccId) &&
@@ -4239,7 +4242,7 @@
sb.setLength(0);
sb.append("PHONE_NUMBERS_EQUAL(" + Tables.DATA + "." + Phone.NUMBER + ", ");
DatabaseUtils.appendEscapedSQLString(sb, number);
- sb.append(mUseStrictPhoneNumberComparison ? ", 1)" : ", 0)");
+ sb.append(mUseStrictPhoneNumberComparison ? ", 1)" : ", 0, " + mMinMatch + ")");
qb.appendWhere(sb.toString());
}
@@ -4336,6 +4339,10 @@
return mUseStrictPhoneNumberComparison ? "1" : "0";
}
+ public String getMinMatchParameter() {
+ return String.valueOf(mMinMatch);
+ }
+
/**
* Loads common nickname mappings into the database.
*/
@@ -4954,6 +4961,16 @@
return mUseStrictPhoneNumberComparison;
}
+ @VisibleForTesting
+ public void setMinMatchForTest(int minMatch) {
+ mMinMatch = minMatch;
+ }
+
+ @VisibleForTesting
+ public int getMinMatchForTest() {
+ return mMinMatch;
+ }
+
@NeededForTesting
/* package */ String querySearchIndexContentForTest(long contactId) {
return DatabaseUtils.stringForQuery(getReadableDatabase(),
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index 02c8bf0..5159fb9 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -16,6 +16,10 @@
package com.android.providers.contacts;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.OnAccountsUpdateListener;
@@ -214,7 +218,6 @@
private static final String READ_PERMISSION = "android.permission.READ_CONTACTS";
private static final String WRITE_PERMISSION = "android.permission.WRITE_CONTACTS";
- private static final String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS";
/* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
@@ -532,23 +535,6 @@
" SET " + RawContacts.DIRTY + "=1" +
" WHERE " + RawContacts._ID + " IN (";
- /** Sql for updating METADATA_DIRTY flag on multiple raw contacts */
- private static final String UPDATE_RAW_CONTACT_SET_METADATA_DIRTY_SQL =
- "UPDATE " + Tables.RAW_CONTACTS +
- " SET " + RawContacts.METADATA_DIRTY + "=1" +
- " WHERE " + RawContacts._ID + " IN (";
-
- // Sql for updating MetadataSync.DELETED flag on multiple raw contacts.
- // When using this sql, add comma separated raw contacts ids and "))".
- private static final String UPDATE_METADATASYNC_SET_DELETED_SQL =
- "UPDATE " + Tables.METADATA_SYNC
- + " SET " + MetadataSync.DELETED + "=1"
- + " WHERE " + MetadataSync._ID + " IN "
- + "(SELECT " + MetadataSyncColumns.CONCRETE_ID
- + " FROM " + Tables.RAW_CONTACTS_JOIN_METADATA_SYNC
- + " WHERE " + RawContactsColumns.CONCRETE_DELETED + "=1 AND "
- + RawContactsColumns.CONCRETE_ID + " IN (";
-
/** Sql for updating VERSION on multiple raw contacts */
private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
"UPDATE " + Tables.RAW_CONTACTS +
@@ -2433,14 +2419,6 @@
for (long rawContactId : mTransactionContext.get().getInsertedRawContactIds()) {
mDbHelper.get().updateRawContactDisplayName(db, rawContactId);
mAggregator.get().onRawContactInsert(mTransactionContext.get(), db, rawContactId);
- if (mMetadataSyncEnabled) {
- updateMetadataOnRawContactInsert(db, rawContactId);
- }
- }
- if (mMetadataSyncEnabled) {
- for (long rawContactId : mTransactionContext.get().getBackupIdChangedRawContacts()) {
- updateMetadataOnRawContactInsert(db, rawContactId);
- }
}
final Set<Long> dirtyRawContacts = mTransactionContext.get().getDirtyRawContactIds();
@@ -2461,29 +2439,8 @@
db.execSQL(mSb.toString());
}
- final Set<Long> metadataDirtyRawContacts =
- mTransactionContext.get().getMetadataDirtyRawContactIds();
- if (!metadataDirtyRawContacts.isEmpty() && mMetadataSyncEnabled) {
- mSb.setLength(0);
- mSb.append(UPDATE_RAW_CONTACT_SET_METADATA_DIRTY_SQL);
- appendIds(mSb, metadataDirtyRawContacts);
- mSb.append(")");
- db.execSQL(mSb.toString());
- mSyncToMetadataNetWork = true;
- }
-
final Set<Long> changedRawContacts = mTransactionContext.get().getChangedRawContactIds();
ContactsTableUtil.updateContactLastUpdateByRawContactId(db, changedRawContacts);
- if (!changedRawContacts.isEmpty() && mMetadataSyncEnabled) {
- // For the deleted raw contact, set related metadata as deleted
- // if metadata flag is enabled.
- mSb.setLength(0);
- mSb.append(UPDATE_METADATASYNC_SET_DELETED_SQL);
- appendIds(mSb, changedRawContacts);
- mSb.append("))");
- db.execSQL(mSb.toString());
- mSyncToMetadataNetWork = true;
- }
// Update sync states.
for (Map.Entry<Long, Object> entry : mTransactionContext.get().getUpdatedSyncStates()) {
@@ -2502,49 +2459,6 @@
mMetadataSyncEnabled = enabled;
}
- interface MetadataSyncQuery {
- String TABLE = Tables.RAW_CONTACTS_JOIN_METADATA_SYNC;
- String[] COLUMNS = new String[] {
- MetadataSyncColumns.CONCRETE_ID,
- MetadataSync.DATA
- };
- int METADATA_SYNC_ID = 0;
- int METADATA_SYNC_DATA = 1;
- String SELECTION = MetadataSyncColumns.CONCRETE_DELETED + "=0 AND " +
- RawContactsColumns.CONCRETE_ID + "=?";
- }
-
- /**
- * Fetch the related metadataSync data column for the raw contact id.
- * Returns null if there's no metadata for the raw contact.
- */
- private String queryMetadataSyncData(SQLiteDatabase db, long rawContactId) {
- String metadataSyncData = null;
- mSelectionArgs1[0] = String.valueOf(rawContactId);
- final Cursor cursor = db.query(MetadataSyncQuery.TABLE,
- MetadataSyncQuery.COLUMNS, MetadataSyncQuery.SELECTION,
- mSelectionArgs1, null, null, null);
- try {
- if (cursor.moveToFirst()) {
- metadataSyncData = cursor.getString(MetadataSyncQuery.METADATA_SYNC_DATA);
- }
- } finally {
- cursor.close();
- }
- return metadataSyncData;
- }
-
- private void updateMetadataOnRawContactInsert(SQLiteDatabase db, long rawContactId) {
- // Read metadata from MetadataSync table for the raw contact, and update.
- final String metadataSyncData = queryMetadataSyncData(db, rawContactId);
- if (TextUtils.isEmpty(metadataSyncData)) {
- return;
- }
- final MetadataEntry metadataEntry = MetadataEntryParser.parseDataToMetaDataEntry(
- metadataSyncData);
- updateFromMetaDataEntry(db, metadataEntry);
- }
-
/**
* Appends comma separated IDs.
* @param ids Should not be empty
@@ -2566,10 +2480,7 @@
protected void notifyChange(boolean syncToNetwork, boolean syncToMetadataNetwork) {
getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
- syncToNetwork || syncToMetadataNetwork);
-
- getContext().getContentResolver().notifyChange(MetadataSync.METADATA_AUTHORITY_URI,
- null, syncToMetadataNetwork);
+ syncToNetwork);
}
protected void setProviderStatus(int status) {
@@ -2606,7 +2517,8 @@
protected Uri insertInTransaction(Uri uri, ContentValues values) {
if (VERBOSE_LOGGING) {
Log.v(TAG, "insertInTransaction: uri=" + uri + " values=[" + values + "]" +
- " CPID=" + Binder.getCallingPid());
+ " CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid());
}
final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
@@ -2836,7 +2748,6 @@
values.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
}
- final boolean needToUpdateMetadata = shouldMarkMetadataDirtyForRawContact(values);
// Databases that were created prior to the 906 upgrade have a default of Int.MAX_VALUE
// for RawContacts.PINNED. Manually set the value to the correct default (0) if it is not
// set.
@@ -2848,14 +2759,6 @@
final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
final long rawContactId = db.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, values);
- if (needToUpdateMetadata) {
- mTransactionContext.get().markRawContactMetadataDirty(rawContactId,
- /* isMetadataSyncAdapter =*/false);
- }
- // 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.
- mSyncToMetadataNetWork |= callerIsSyncAdapter;
-
final int aggregationMode = getIntValue(values, RawContacts.AGGREGATION_MODE,
RawContacts.AGGREGATION_MODE_DEFAULT);
mAggregator.get().markNewForAggregation(rawContactId, aggregationMode);
@@ -3550,6 +3453,7 @@
Log.v(TAG, "deleteInTransaction: uri=" + uri +
" selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) +
" CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid() +
" User=" + UserUtils.getCurrentUserHandle(getContext()));
}
@@ -3802,15 +3706,25 @@
}
private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
+ ArrayList<Long> localRawContactIds = new ArrayList();
+
final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
mSelectionArgs1[0] = Long.toString(contactId);
Cursor c = db.query(Tables.RAW_CONTACTS, new String[] {RawContacts._ID},
RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
null, null, null);
+
+ // Raw contacts need to be deleted after the contact so just loop through and mark
+ // non-local raw contacts as deleted and collect the local raw contacts that will be
+ // deleted after the contact is deleted.
try {
while (c.moveToNext()) {
long rawContactId = c.getLong(0);
- markRawContactAsDeleted(db, rawContactId, callerIsSyncAdapter);
+ if (rawContactIsLocal(rawContactId)) {
+ localRawContactIds.add(rawContactId);
+ } else {
+ markRawContactAsDeleted(db, rawContactId, callerIsSyncAdapter);
+ }
}
} finally {
c.close();
@@ -3819,6 +3733,10 @@
mProviderStatusUpdateNeeded = true;
int result = ContactsTableUtil.deleteContact(db, contactId);
+
+ // Now purge the local raw contacts
+ deleteRawContactsImmediately(db, localRawContactIds);
+
scheduleBackgroundTask(BACKGROUND_TASK_CLEAN_DELETE_LOG);
return result;
}
@@ -3842,19 +3760,19 @@
c.close();
}
+ // When a raw contact is deleted, a sqlite trigger deletes the parent contact.
+ // TODO: all contact deletes was consolidated into ContactTableUtil but this one can't
+ // because it's in a trigger. Consider removing trigger and replacing with java code.
+ // This has to happen before the raw contact is deleted since it relies on the number
+ // of raw contacts.
final boolean contactIsSingleton =
ContactsTableUtil.deleteContactIfSingleton(db, rawContactId) == 1;
final int count;
if (callerIsSyncAdapter || rawContactIsLocal(rawContactId)) {
- // When a raw contact is deleted, a SQLite trigger deletes the parent contact.
- // TODO: all contact deletes was consolidated into ContactTableUtil but this one can't
- // because it's in a trigger. Consider removing trigger and replacing with java code.
- // This has to happen before the raw contact is deleted since it relies on the number
- // of raw contacts.
- db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
- count = db.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
- mTransactionContext.get().markRawContactChangedOrDeletedOrInserted(rawContactId);
+ ArrayList<Long> rawContactsIds = new ArrayList<>();
+ rawContactsIds.add(rawContactId);
+ count = deleteRawContactsImmediately(db, rawContactsIds);
} else {
count = markRawContactAsDeleted(db, rawContactId, callerIsSyncAdapter);
}
@@ -3865,6 +3783,43 @@
}
/**
+ * Returns the number of raw contacts that were deleted immediately -- we don't merely set
+ * the DELETED column to 1, the entire raw contact row is deleted straightaway.
+ */
+ private int deleteRawContactsImmediately(SQLiteDatabase db, List<Long> rawContactIds) {
+ if (rawContactIds == null || rawContactIds.isEmpty()) {
+ return 0;
+ }
+
+ // Build the where clause for the raw contacts to be deleted
+ ArrayList<String> whereArgs = new ArrayList<>();
+ StringBuilder whereClause = new StringBuilder(rawContactIds.size() * 2 - 1);
+ whereClause.append(" IN (?");
+ whereArgs.add(String.valueOf(rawContactIds.get(0)));
+ for (int i = 1; i < rawContactIds.size(); i++) {
+ whereClause.append(",?");
+ whereArgs.add(String.valueOf(rawContactIds.get(i)));
+ }
+ whereClause.append(")");
+
+ // Remove presence rows
+ db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + whereClause.toString(),
+ whereArgs.toArray(new String[0]));
+
+ // Remove raw contact rows
+ int result = db.delete(Tables.RAW_CONTACTS, RawContacts._ID + whereClause.toString(),
+ whereArgs.toArray(new String[0]));
+
+ if (result > 0) {
+ for (Long rawContactId : rawContactIds) {
+ mTransactionContext.get().markRawContactChangedOrDeletedOrInserted(rawContactId);
+ }
+ }
+
+ return result;
+ }
+
+ /**
* Returns whether the given raw contact ID is local (i.e. has no account associated with it).
*/
private boolean rawContactIsLocal(long rawContactId) {
@@ -3964,6 +3919,7 @@
Log.v(TAG, "updateInTransaction: uri=" + uri +
" selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) +
" values=[" + values + "] CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid() +
" User=" + UserUtils.getCurrentUserHandle(getContext()));
}
@@ -4498,7 +4454,6 @@
final boolean isDataSetChanging = values.containsKey(RawContacts.DATA_SET);
final boolean isAccountChanging =
isAccountNameChanging || isAccountTypeChanging || isDataSetChanging;
- final boolean isBackupIdChanging = values.containsKey(RawContacts.BACKUP_ID);
int previousDeleted = 0;
long accountId = 0;
@@ -4561,32 +4516,6 @@
if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
aggregator.markForAggregation(rawContactId, aggregationMode, false);
}
- if (shouldMarkMetadataDirtyForRawContact(values)) {
- mTransactionContext.get().markRawContactMetadataDirty(
- rawContactId, callerIsMetadataSyncAdapter);
- }
- if (isBackupIdChanging) {
- Cursor cursor = db.query(Tables.RAW_CONTACTS,
- new String[] {RawContactsColumns.CONCRETE_METADATA_DIRTY},
- selection, mSelectionArgs1, null, null, null);
- int metadataDirty = 0;
- try {
- if (cursor.moveToFirst()) {
- metadataDirty = cursor.getInt(0);
- }
- } finally {
- cursor.close();
- }
-
- if (metadataDirty == 1) {
- // Re-notify metadata network if backup_id is updated and metadata is dirty.
- mTransactionContext.get().markRawContactMetadataDirty(
- rawContactId, callerIsMetadataSyncAdapter);
- } else {
- // Merge from metadata sync table if backup_id is updated and no dirty change.
- mTransactionContext.get().markBackupIdChangedRawContact(rawContactId);
- }
- }
if (flagExists(values, RawContacts.STARRED)) {
if (!callerIsSyncAdapter) {
updateFavoritesMembership(rawContactId, flagIsSet(values, RawContacts.STARRED));
@@ -4760,10 +4689,6 @@
// Mark dirty when changing starred to trigger sync.
values.put(RawContacts.DIRTY, 1);
}
- if (mMetadataSyncEnabled && (hasStarredValue || hasPinnedValue || hasVoiceMailValue)) {
- // Mark dirty to trigger metadata syncing.
- values.put(RawContacts.METADATA_DIRTY, 1);
- }
mSelectionArgs1[0] = String.valueOf(contactId);
db.update(Tables.RAW_CONTACTS, values, RawContacts.CONTACT_ID + "=?"
@@ -4781,11 +4706,6 @@
flagIsSet(values, RawContacts.STARRED));
mSyncToNetwork |= !callerIsSyncAdapter;
}
-
- if (hasStarredValue || hasPinnedValue || hasVoiceMailValue) {
- mTransactionContext.get().markRawContactMetadataDirty(rawContactId,
- false /*callerIsMetadataSyncAdapter*/);
- }
}
} finally {
cursor.close();
@@ -4863,21 +4783,12 @@
aggregator.aggregateContact(mTransactionContext.get(), db, rawContactId1);
aggregator.aggregateContact(mTransactionContext.get(), db, rawContactId2);
- mTransactionContext.get().markRawContactMetadataDirty(rawContactId1,
- callerIsMetadataSyncAdapter);
- mTransactionContext.get().markRawContactMetadataDirty(rawContactId2,
- callerIsMetadataSyncAdapter);
// The return value is fake - we just confirm that we made a change, not count actual
// rows changed.
return 1;
}
- private boolean shouldMarkMetadataDirtyForRawContact(ContentValues values) {
- return (flagExists(values, RawContacts.STARRED) || flagExists(values, RawContacts.PINNED)
- || flagExists(values, RawContacts.SEND_TO_VOICEMAIL));
- }
-
@Override
public void onAccountsUpdated(Account[] accounts) {
scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
@@ -5441,6 +5352,7 @@
Log.v(TAG, "query: uri=" + uri + " projection=" + Arrays.toString(projection) +
" selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) +
" order=[" + sortOrder + "] CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid() +
" User=" + UserUtils.getCurrentUserHandle(getContext()));
}
@@ -5454,10 +5366,12 @@
return null;
}
- // Check enterprise policy if caller does not come from same profile
- if (!(isCallerFromSameUser() || mEnterprisePolicyGuard.isCrossProfileAllowed(uri))) {
- return createEmptyCursor(uri, projection);
+ // If caller does not come from same profile, Check if it's privileged or allowed by
+ // enterprise policy
+ if (!queryAllowedByEnterprisePolicy(uri)) {
+ return null;
}
+
// Query the profile DB if appropriate.
if (mapsToProfileDb(uri)) {
switchToProfileMode();
@@ -5477,9 +5391,46 @@
}
}
+ private boolean queryAllowedByEnterprisePolicy(Uri uri) {
+ if (isCallerFromSameUser()) {
+ // Caller is on the same user; query allowed.
+ return true;
+ }
+ if (!doesCallerHoldInteractAcrossUserPermission()) {
+ // Cross-user and the caller has no INTERACT_ACROSS_USERS; don't allow query.
+ // Technically, in a cross-profile sharing case, this would be a valid query.
+ // But for now we don't allow it. (We never allowe it and no one complained about it.)
+ return false;
+ }
+ if (isCallerAnotherSelf()) {
+ // The caller is the other CP2 (which has INTERACT_ACROSS_USERS), meaning the reuest
+ // is on behalf of a "real" client app.
+ // Consult the enterprise policy.
+ return mEnterprisePolicyGuard.isCrossProfileAllowed(uri);
+ }
+ return true;
+ }
+
private boolean isCallerFromSameUser() {
- return Binder.getCallingUserHandle().getIdentifier() == UserUtils
- .getCurrentUserHandle(getContext());
+ return UserHandle.getUserId(Binder.getCallingUid()) == UserHandle.myUserId();
+ }
+
+ /**
+ * Returns true if called by a different user's CP2.
+ */
+ private boolean isCallerAnotherSelf() {
+ // Note normally myUid is always different from the callerUid in the code path where
+ // this method is used, except during unit tests, where the caller is always the same
+ // process.
+ final int myUid = android.os.Process.myUid();
+ final int callingUid = Binder.getCallingUid();
+ return (myUid != callingUid) && UserHandle.isSameApp(myUid, callingUid);
+ }
+
+ private boolean doesCallerHoldInteractAcrossUserPermission() {
+ final Context context = getContext();
+ return context.checkCallingPermission(INTERACT_ACROSS_USERS_FULL) == PERMISSION_GRANTED
+ || context.checkCallingPermission(INTERACT_ACROSS_USERS) == PERMISSION_GRANTED;
}
private Cursor queryDirectoryIfNecessary(Uri uri, String[] projection, String selection,
@@ -5526,7 +5477,7 @@
private String getRealCallerPackageName(Uri queryUri) {
// If called by another CP2, then the URI should contain the original package name.
- if (calledByAnotherSelf()) {
+ if (isCallerAnotherSelf()) {
final String passedPackage = queryUri.getQueryParameter(
Directory.CALLER_PACKAGE_PARAM_KEY);
if (TextUtils.isEmpty(passedPackage)) {
@@ -5541,18 +5492,6 @@
}
}
- /**
- * Returns true if called by a different user's CP2.
- */
- private boolean calledByAnotherSelf() {
- // Note normally myUid is always different from the callerUid in the code path where
- // this method is used, except during unit tests, where the caller is always the same
- // process.
- final int myUid = android.os.Process.myUid();
- final int callerUid = Binder.getCallingUid();
- return (myUid != callerUid) && UserHandle.isSameApp(myUid, callerUid);
- }
-
private Cursor queryDirectoryAuthority(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder, String directory,
final CancellationSignal cancellationSignal) {
@@ -8525,9 +8464,7 @@
if (!isDirectoryParamValid(uri)){
return null;
}
- if (!isCallerFromSameUser() /* From differnt user */
- && !mEnterprisePolicyGuard.isCrossProfileAllowed(uri)
- /* Policy not allowed */){
+ if (!queryAllowedByEnterprisePolicy(uri)) {
return null;
}
waitForAccess(mode.equals("r") ? mReadAccessLatch : mWriteAccessLatch);
@@ -8545,6 +8482,7 @@
if (VERBOSE_LOGGING) {
Log.v(TAG, "openAssetFile uri=" + uri + " mode=" + mode + " success=" + success +
" CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid() +
" User=" + UserUtils.getCurrentUserHandle(getContext()));
}
}
@@ -9664,24 +9602,15 @@
@VisibleForTesting
protected boolean isPhone() {
if (!mIsPhoneInitialized) {
- mIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
+ mIsPhone = isVoiceCapable();
mIsPhoneInitialized = true;
}
return mIsPhone;
}
protected boolean isVoiceCapable() {
- // this copied from com.android.phone.PhoneApp.onCreate():
-
- // "voice capable" flag.
- // This flag currently comes from a resource (which is
- // overrideable on a per-product basis):
- return getContext().getResources()
- .getBoolean(com.android.internal.R.bool.config_voice_capable);
- // ...but this might eventually become a PackageManager "system
- // feature" instead, in which case we'd do something like:
- // return
- // getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_VOICE_CALLS);
+ TelephonyManager tm = getContext().getSystemService(TelephonyManager.class);
+ return tm.isVoiceCapable();
}
private void undemoteContact(SQLiteDatabase db, long id) {
@@ -9828,7 +9757,7 @@
* @return the currently active {@link ContactsDatabaseHelper} for the current thread.
*/
@NeededForTesting
- protected ContactsDatabaseHelper getThreadActiveDatabaseHelperForTest() {
+ public ContactsDatabaseHelper getThreadActiveDatabaseHelperForTest() {
return mDbHelper.get();
}
diff --git a/src/com/android/providers/contacts/MetadataEntryParser.java b/src/com/android/providers/contacts/MetadataEntryParser.java
index 1a630a3..2fe423a 100644
--- a/src/com/android/providers/contacts/MetadataEntryParser.java
+++ b/src/com/android/providers/contacts/MetadataEntryParser.java
@@ -60,8 +60,11 @@
@NeededForTesting
public static class UsageStats {
+ @NeededForTesting
final String mUsageType;
+ @NeededForTesting
final long mLastTimeUsed;
+ @NeededForTesting
final int mTimesUsed;
@NeededForTesting
@@ -74,9 +77,13 @@
@NeededForTesting
public static class FieldData {
+ @NeededForTesting
final String mDataHashId;
+ @NeededForTesting
final boolean mIsPrimary;
+ @NeededForTesting
final boolean mIsSuperPrimary;
+ @NeededForTesting
final ArrayList<UsageStats> mUsageStatsList;
@NeededForTesting
@@ -91,9 +98,13 @@
@NeededForTesting
public static class RawContactInfo {
+ @NeededForTesting
final String mBackupId;
+ @NeededForTesting
final String mAccountType;
+ @NeededForTesting
final String mAccountName;
+ @NeededForTesting
final String mDataSet;
@NeededForTesting
@@ -108,8 +119,11 @@
@NeededForTesting
public static class AggregationData {
+ @NeededForTesting
final RawContactInfo mRawContactInfo1;
+ @NeededForTesting
final RawContactInfo mRawContactInfo2;
+ @NeededForTesting
final String mType;
@NeededForTesting
@@ -123,11 +137,17 @@
@NeededForTesting
public static class MetadataEntry {
+ @NeededForTesting
final RawContactInfo mRawContactInfo;
+ @NeededForTesting
final int mSendToVoicemail;
+ @NeededForTesting
final int mStarred;
+ @NeededForTesting
final int mPinned;
+ @NeededForTesting
final ArrayList<FieldData> mFieldDatas;
+ @NeededForTesting
final ArrayList<AggregationData> mAggregationDatas;
@NeededForTesting
diff --git a/src/com/android/providers/contacts/VoicemailContentProvider.java b/src/com/android/providers/contacts/VoicemailContentProvider.java
index 1ced1be..daecd97 100644
--- a/src/com/android/providers/contacts/VoicemailContentProvider.java
+++ b/src/com/android/providers/contacts/VoicemailContentProvider.java
@@ -22,6 +22,7 @@
import static com.android.providers.contacts.util.DbQueryUtils.getEqualityClause;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.ContentProvider;
import android.content.ContentValues;
@@ -118,24 +119,24 @@
}
@Override
- protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken)
- throws SecurityException {
+ protected int enforceReadPermissionInner(Uri uri, String callingPkg,
+ @Nullable String featureId, IBinder callerToken) throws SecurityException {
// Permit carrier-privileged apps regardless of ADD_VOICEMAIL permission state.
if (mVoicemailPermissions.callerHasCarrierPrivileges()) {
return MODE_ALLOWED;
}
- return super.enforceReadPermissionInner(uri, callingPkg, callerToken);
+ return super.enforceReadPermissionInner(uri, callingPkg, featureId, callerToken);
}
@Override
- protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken)
- throws SecurityException {
+ protected int enforceWritePermissionInner(Uri uri, String callingPkg,
+ @Nullable String featureId, IBinder callerToken) throws SecurityException {
// Permit carrier-privileged apps regardless of ADD_VOICEMAIL permission state.
if (mVoicemailPermissions.callerHasCarrierPrivileges()) {
return MODE_ALLOWED;
}
- return super.enforceWritePermissionInner(uri, callingPkg, callerToken);
+ return super.enforceWritePermissionInner(uri, callingPkg, featureId, callerToken);
}
@VisibleForTesting
@@ -179,7 +180,8 @@
public Uri insert(Uri uri, ContentValues values) {
if (VERBOSE_LOGGING) {
Log.v(TAG, "insert: uri=" + uri + " values=[" + values + "]" +
- " CPID=" + Binder.getCallingPid());
+ " CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid());
}
UriData uriData = checkPermissionsAndCreateUriDataForWrite(uri, values);
return getTableDelegate(uriData).insert(uriData, values);
@@ -198,6 +200,7 @@
Log.v(TAG, "query: uri=" + uri + " projection=" + Arrays.toString(projection) +
" selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) +
" order=[" + sortOrder + "] CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid() +
" User=" + UserUtils.getCurrentUserHandle(getContext()));
}
UriData uriData = checkPermissionsAndCreateUriDataForRead(uri);
@@ -213,6 +216,7 @@
Log.v(TAG, "update: uri=" + uri +
" selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) +
" values=[" + values + "] CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid() +
" User=" + UserUtils.getCurrentUserHandle(getContext()));
}
UriData uriData = checkPermissionsAndCreateUriDataForWrite(uri, values);
@@ -228,6 +232,7 @@
Log.v(TAG, "delete: uri=" + uri +
" selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) +
" CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid() +
" User=" + UserUtils.getCurrentUserHandle(getContext()));
}
UriData uriData = checkPermissionsAndCreateUriDataForWrite(uri);
@@ -254,6 +259,7 @@
if (VERBOSE_LOGGING) {
Log.v(TAG, "openFile uri=" + uri + " mode=" + mode + " success=" + success +
" CPID=" + Binder.getCallingPid() +
+ " CUID=" + Binder.getCallingUid() +
" User=" + UserUtils.getCurrentUserHandle(getContext()));
}
}
diff --git a/src/com/android/providers/contacts/VoicemailPermissions.java b/src/com/android/providers/contacts/VoicemailPermissions.java
index 7e409ea..58e7a14 100644
--- a/src/com/android/providers/contacts/VoicemailPermissions.java
+++ b/src/com/android/providers/contacts/VoicemailPermissions.java
@@ -18,8 +18,9 @@
import android.content.Context;
import android.os.Binder;
-import android.telecom.DefaultDialerManager;
+import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import com.android.providers.contacts.util.ContactsPermissions;
@@ -41,9 +42,20 @@
|| callerHasCarrierPrivileges();
}
+ private boolean isDefaultOrSystemDialer(String callingPackage) {
+ // Note: Mimics previous dependency on DefaultDialerManager; that code just returns false
+ // here if the calling package is empty.
+ if (TextUtils.isEmpty(callingPackage)) {
+ return false;
+ }
+ TelecomManager tm = mContext.getSystemService(TelecomManager.class);
+ return (callingPackage.equals(tm.getDefaultDialerPackage())
+ || callingPackage.equals(tm.getSystemDialerPackage()));
+ }
+
/** Determine if the calling process has full read access to all voicemails. */
public boolean callerHasReadAccess(String callingPackage) {
- if (DefaultDialerManager.isDefaultOrSystemDialer(mContext, callingPackage)) {
+ if (isDefaultOrSystemDialer(callingPackage)) {
return true;
}
return callerHasPermission(android.Manifest.permission.READ_VOICEMAIL);
@@ -52,7 +64,7 @@
/** Determine if the calling process has the permission required to update and remove all
* voicemails */
public boolean callerHasWriteAccess(String callingPackage) {
- if (DefaultDialerManager.isDefaultOrSystemDialer(mContext, callingPackage)) {
+ if (isDefaultOrSystemDialer(callingPackage)) {
return true;
}
return callerHasPermission(android.Manifest.permission.WRITE_VOICEMAIL);
diff --git a/src/com/android/providers/contacts/aggregation/AbstractContactAggregator.java b/src/com/android/providers/contacts/aggregation/AbstractContactAggregator.java
index 965780e..82e35f1 100644
--- a/src/com/android/providers/contacts/aggregation/AbstractContactAggregator.java
+++ b/src/com/android/providers/contacts/aggregation/AbstractContactAggregator.java
@@ -174,6 +174,7 @@
protected String[] mSelectionArgs1 = new String[1];
protected String[] mSelectionArgs2 = new String[2];
+ protected String[] mSelectionArgs3 = new String[3];
protected long mMimeTypeIdIdentity;
protected long mMimeTypeIdEmail;
@@ -794,8 +795,8 @@
" AND d1." + Data.RAW_CONTACT_ID + " IN (" + rawContactIdSet1 + ")" +
" AND d2." + Data.RAW_CONTACT_ID + " IN (" + rawContactIdSet2 + ")" +
" AND PHONE_NUMBERS_EQUAL(d1." + Phone.NUMBER + ",d2." + Phone.NUMBER + "," +
- String.valueOf(mDbHelper.getUseStrictPhoneNumberComparisonParameter()) +
- ")";
+ (mDbHelper.getUseStrictPhoneNumberComparisonParameter().equals("1") ? "1)"
+ : "0," + mDbHelper.getMinMatchParameter() + ")");
return (countOnly) ? RawContactMatchingSelectionStatement.SELECT_COUNT + sql :
RawContactMatchingSelectionStatement.SELECT_ID + sql;
}
@@ -1195,6 +1196,12 @@
+ " AND " + RawContactsColumns.AGGREGATION_NEEDED + "=0"
+ " AND " + RawContacts.CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY;
+ String SELECTION_MIN_MATCH = "dataA." + Data.RAW_CONTACT_ID + "=?"
+ + " AND PHONE_NUMBERS_EQUAL(dataA." + Phone.NUMBER + ", "
+ + "dataB." + Phone.NUMBER + ",?,?)"
+ + " AND " + RawContactsColumns.AGGREGATION_NEEDED + "=0"
+ + " AND " + RawContacts.CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY;
+
String[] COLUMNS = new String[] {
Tables.RAW_CONTACTS + "." + RawContacts._ID,
RawContacts.CONTACT_ID,
diff --git a/src/com/android/providers/contacts/aggregation/ContactAggregator.java b/src/com/android/providers/contacts/aggregation/ContactAggregator.java
index e5bd2ea..a104339 100644
--- a/src/com/android/providers/contacts/aggregation/ContactAggregator.java
+++ b/src/com/android/providers/contacts/aggregation/ContactAggregator.java
@@ -800,11 +800,25 @@
private void updateMatchScoresBasedOnPhoneMatches(SQLiteDatabase db, long rawContactId,
ContactMatcher matcher) {
- mSelectionArgs2[0] = String.valueOf(rawContactId);
- mSelectionArgs2[1] = mDbHelper.getUseStrictPhoneNumberComparisonParameter();
- Cursor c = db.query(PhoneLookupQuery.TABLE, PhoneLookupQuery.COLUMNS,
+ Cursor c;
+ String useStrictPhoneNumberComparison =
+ mDbHelper.getUseStrictPhoneNumberComparisonParameter();
+
+ if (useStrictPhoneNumberComparison.equals("1")) {
+ mSelectionArgs2[0] = String.valueOf(rawContactId);
+ mSelectionArgs2[1] = useStrictPhoneNumberComparison;
+ c = db.query(PhoneLookupQuery.TABLE, PhoneLookupQuery.COLUMNS,
PhoneLookupQuery.SELECTION,
mSelectionArgs2, null, null, null, SECONDARY_HIT_LIMIT_STRING);
+ } else {
+ mSelectionArgs3[0] = String.valueOf(rawContactId);
+ mSelectionArgs3[1] = useStrictPhoneNumberComparison;
+ mSelectionArgs3[2] = mDbHelper.getMinMatchParameter();
+ c = db.query(PhoneLookupQuery.TABLE, PhoneLookupQuery.COLUMNS,
+ PhoneLookupQuery.SELECTION_MIN_MATCH,
+ mSelectionArgs3, null, null, null, SECONDARY_HIT_LIMIT_STRING);
+ }
+
try {
while (c.moveToNext()) {
long contactId = c.getLong(PhoneLookupQuery.CONTACT_ID);
diff --git a/src/com/android/providers/contacts/aggregation/ContactAggregator2.java b/src/com/android/providers/contacts/aggregation/ContactAggregator2.java
index e0bc3bb..6b1424c 100644
--- a/src/com/android/providers/contacts/aggregation/ContactAggregator2.java
+++ b/src/com/android/providers/contacts/aggregation/ContactAggregator2.java
@@ -665,11 +665,25 @@
private void updateMatchScoresBasedOnPhoneMatches(SQLiteDatabase db, long rawContactId,
RawContactMatcher matcher) {
- mSelectionArgs2[0] = String.valueOf(rawContactId);
- mSelectionArgs2[1] = mDbHelper.getUseStrictPhoneNumberComparisonParameter();
- Cursor c = db.query(PhoneLookupQuery.TABLE, PhoneLookupQuery.COLUMNS,
+ Cursor c;
+ String useStrictPhoneNumberComparison =
+ mDbHelper.getUseStrictPhoneNumberComparisonParameter();
+
+ if (useStrictPhoneNumberComparison.equals("1")) {
+ mSelectionArgs2[0] = String.valueOf(rawContactId);
+ mSelectionArgs2[1] = useStrictPhoneNumberComparison;
+ c = db.query(PhoneLookupQuery.TABLE, PhoneLookupQuery.COLUMNS,
PhoneLookupQuery.SELECTION,
mSelectionArgs2, null, null, null, SECONDARY_HIT_LIMIT_STRING);
+ } else {
+ mSelectionArgs3[0] = String.valueOf(rawContactId);
+ mSelectionArgs3[1] = useStrictPhoneNumberComparison;
+ mSelectionArgs3[2] = mDbHelper.getMinMatchParameter();
+ c = db.query(PhoneLookupQuery.TABLE, PhoneLookupQuery.COLUMNS,
+ PhoneLookupQuery.SELECTION_MIN_MATCH,
+ mSelectionArgs3, null, null, null, SECONDARY_HIT_LIMIT_STRING);
+ }
+
try {
while (c.moveToNext()) {
long rId = c.getLong(PhoneLookupQuery.RAW_CONTACT_ID);
@@ -1029,6 +1043,11 @@
+ "dataB." + Phone.NUMBER + ",?)"
+ " AND " + RawContacts.CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY;
+ String SELECTION_MIN_MATCH = "dataA." + Data.RAW_CONTACT_ID + "=?"
+ + " AND PHONE_NUMBERS_EQUAL(dataA." + Phone.NUMBER + ", "
+ + "dataB." + Phone.NUMBER + ",?,?)"
+ + " AND " + RawContacts.CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY;
+
String[] COLUMNS = new String[] {
Tables.RAW_CONTACTS + "." + RawContacts._ID,
RawContacts.CONTACT_ID,
diff --git a/test_common/src/com/android/providers/contacts/testutil/DatabaseAsserts.java b/test_common/src/com/android/providers/contacts/testutil/DatabaseAsserts.java
index 2af9829..e0a7836 100644
--- a/test_common/src/com/android/providers/contacts/testutil/DatabaseAsserts.java
+++ b/test_common/src/com/android/providers/contacts/testutil/DatabaseAsserts.java
@@ -16,6 +16,7 @@
package com.android.providers.contacts.testutil;
+import android.accounts.Account;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.net.Uri;
@@ -50,12 +51,21 @@
}
/**
- * Create a contact and assert that the record exists.
+ * Create a contact in the local account and assert that the record exists.
*
* @return The created contact id pair.
*/
public static ContactIdPair assertAndCreateContact(ContentResolver resolver) {
- long rawContactId = RawContactUtil.createRawContactWithName(resolver);
+ return assertAndCreateContact(resolver, null);
+ }
+
+ /**
+ * Create a contact in the given account and assert that the record exists.
+ *
+ * @return The created contact id pair.
+ */
+ public static ContactIdPair assertAndCreateContact(ContentResolver resolver, Account account) {
+ long rawContactId = RawContactUtil.createRawContactWithName(resolver, account);
long contactId = RawContactUtil.queryContactIdByRawContactId(resolver, rawContactId);
MoreAsserts.assertNotEqual(CommonDatabaseUtils.NOT_FOUND, contactId);
diff --git a/tests/src/com/android/providers/contacts/CallLogProviderTest.java b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
index 93806e0..9efdfaa 100644
--- a/tests/src/com/android/providers/contacts/CallLogProviderTest.java
+++ b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
@@ -16,9 +16,7 @@
package com.android.providers.contacts;
-import com.android.internal.telephony.CallerInfo;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.providers.contacts.CallLogDatabaseHelper.DbProperties;
+import android.telecom.CallerInfo;
import com.android.providers.contacts.testutil.CommonDatabaseUtils;
import android.content.ComponentName;
@@ -63,6 +61,10 @@
/** Total number of columns exposed by call_log provider. */
private static final int NUM_CALLLOG_FIELDS = 34;
+ private static final int MIN_MATCH = 7;
+
+ private int mOldMinMatch;
+
private CallLogProviderTestable mCallLogProvider;
@Override
@@ -79,6 +81,8 @@
protected void setUp() throws Exception {
super.setUp();
mCallLogProvider = addProvider(CallLogProviderTestable.class, CallLog.AUTHORITY);
+ mOldMinMatch = mCallLogProvider.getMinMatchForTest();
+ mCallLogProvider.setMinMatchForTest(MIN_MATCH);
}
@Override
@@ -86,6 +90,7 @@
setUpWithVoicemailPermissions();
mResolver.delete(Calls.CONTENT_URI_WITH_VOICEMAIL, null, null);
setTimeForTest(null);
+ mCallLogProvider.setMinMatchForTest(mOldMinMatch);
super.tearDown();
}
@@ -180,7 +185,7 @@
public void testAddCall() {
CallerInfo ci = new CallerInfo();
- ci.name = "1-800-GOOG-411";
+ ci.setName("1-800-GOOG-411");
ci.numberType = Phone.TYPE_CUSTOM;
ci.numberLabel = "Directory";
final ComponentName sComponentName = new ComponentName(
@@ -190,7 +195,7 @@
sComponentName, "sub0");
Uri uri = Calls.addCall(ci, getMockContext(), "1-800-263-7643",
- PhoneConstants.PRESENTATION_ALLOWED, Calls.OUTGOING_TYPE, 0, subscription, 2000,
+ Calls.PRESENTATION_ALLOWED, Calls.OUTGOING_TYPE, 0, subscription, 2000,
40, null);
assertNotNull(uri);
assertEquals("0@" + CallLog.AUTHORITY, uri.getAuthority());
@@ -202,7 +207,7 @@
values.put(Calls.NUMBER_PRESENTATION, Calls.PRESENTATION_ALLOWED);
values.put(Calls.DATE, 2000);
values.put(Calls.DURATION, 40);
- values.put(Calls.CACHED_NAME, ci.name);
+ values.put(Calls.CACHED_NAME, ci.getName());
values.put(Calls.CACHED_NUMBER_TYPE, (String) null);
values.put(Calls.CACHED_NUMBER_LABEL, (String) null);
values.put(Calls.COUNTRY_ISO, "us");
diff --git a/tests/src/com/android/providers/contacts/CallerInfoIntegrationTest.java b/tests/src/com/android/providers/contacts/CallerInfoIntegrationTest.java
index 867f01f..f57c060 100644
--- a/tests/src/com/android/providers/contacts/CallerInfoIntegrationTest.java
+++ b/tests/src/com/android/providers/contacts/CallerInfoIntegrationTest.java
@@ -22,7 +22,7 @@
import android.provider.ContactsContract.RawContacts;
import android.test.suitebuilder.annotation.MediumTest;
-import com.android.internal.telephony.CallerInfo;
+import android.telecom.CallerInfo;
import com.android.providers.contacts.testutil.DataUtil;
/**
@@ -49,9 +49,9 @@
insertPhoneNumber(rawContactId, "800-466-4411");
CallerInfo callerInfo = CallerInfo.getCallerInfo(getProvider().getContext(), "18004664411");
- assertEquals("800-466-4411", callerInfo.phoneNumber);
+ assertEquals("800-466-4411", callerInfo.getPhoneNumber());
assertEquals("Home", callerInfo.phoneLabel);
- assertEquals("Hot Tamale", callerInfo.name);
+ assertEquals("Hot Tamale", callerInfo.getName());
assertEquals("ring", String.valueOf(callerInfo.contactRingtoneUri));
assertEquals(true, callerInfo.shouldSendToVoicemail);
assertEquals("content://com.android.contacts/phone_lookup_enterprise/18004664411",
diff --git a/tests/src/com/android/providers/contacts/ContactsActor.java b/tests/src/com/android/providers/contacts/ContactsActor.java
index 470f64b..804e79a 100644
--- a/tests/src/com/android/providers/contacts/ContactsActor.java
+++ b/tests/src/com/android/providers/contacts/ContactsActor.java
@@ -16,6 +16,8 @@
package com.android.providers.contacts;
+import static org.mockito.Mockito.when;
+
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
@@ -57,6 +59,7 @@
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.StatusUpdates;
+import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
import android.test.IsolatedContext;
import android.test.mock.MockContentResolver;
@@ -68,6 +71,8 @@
import com.google.android.collect.Sets;
+import org.mockito.Mockito;
+
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -91,6 +96,8 @@
public static final String PACKAGE_GREEN = "com.example.green";
public static final String PACKAGE_BLUE = "org.example.blue";
+ private static final int DEFAULT_USER_ID = 0;
+
public Context context;
public String packageName;
public MockContentResolver resolver;
@@ -153,11 +160,8 @@
public static class MockUserManager extends UserManager {
public static UserInfo createUserInfo(String name, int id, int groupId, int flags) {
- final UserInfo ui = new UserInfo();
- ui.name = name;
- ui.id = id;
+ final UserInfo ui = new UserInfo(id, name, flags | UserInfo.FLAG_INITIALIZED);
ui.profileGroupId = groupId;
- ui.flags = flags | UserInfo.FLAG_INITIALIZED;
return ui;
}
@@ -168,7 +172,7 @@
public static final UserInfo SECONDARY_USER = createUserInfo("2nd", 11, 11, 0);
/** "My" user. Set it to change the current user. */
- public int myUser = 0;
+ public int myUser = DEFAULT_USER_ID;
private ArrayList<UserInfo> mUsers = new ArrayList<>();
@@ -271,6 +275,8 @@
}
}
+ private TelecomManager mMockTelecomManager;
+
/**
* A context wrapper that reports a different user id.
*
@@ -322,6 +328,9 @@
if (Context.TELEPHONY_SERVICE.equals(name)) {
return mMockTelephonyManager;
}
+ if (Context.TELECOM_SERVICE.equals(name)) {
+ return mMockTelecomManager;
+ }
// Use overallContext here; super.getSystemService() somehow won't return
// DevicePolicyManager.
return overallContext.getSystemService(name);
@@ -369,6 +378,9 @@
if (Context.TELEPHONY_SERVICE.equals(name)) {
return mMockTelephonyManager;
}
+ if (Context.TELECOM_SERVICE.equals(name)) {
+ return mMockTelecomManager;
+ }
// Use overallContext here; super.getSystemService() somehow won't return
// DevicePolicyManager.
return overallContext.getSystemService(name);
@@ -386,7 +398,11 @@
@Override
public int getUserId() {
- return mockUserManager.getUserHandle();
+ if (mockUserManager != null) {
+ return mockUserManager.getUserHandle();
+ } else {
+ return DEFAULT_USER_ID;
+ }
}
@Override
@@ -403,6 +419,9 @@
mMockAccountManager = new MockAccountManager(mProviderContext);
mockUserManager = new MockUserManager(mProviderContext);
mMockTelephonyManager = new MockTelephonyManager(mProviderContext);
+ mMockTelecomManager = Mockito.mock(TelecomManager.class);
+ when(mMockTelecomManager.getDefaultDialerPackage()).thenReturn("");
+ when(mMockTelecomManager.getSystemDialerPackage()).thenReturn("");
provider = addProvider(providerClass, authority);
}
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index 32a0306..d5643d2 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -70,6 +70,7 @@
import android.provider.ContactsContract.StreamItemPhotos;
import android.provider.ContactsContract.StreamItems;
import android.provider.OpenableColumns;
+import android.telephony.PhoneNumberUtils;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.LargeTest;
import android.text.TextUtils;
@@ -129,6 +130,31 @@
private static final String TAG = ContactsProvider2Test.class.getSimpleName();
+ private static final int MIN_MATCH = 7;
+
+ private int mOldMinMatch1;
+ private int mOldMinMatch2;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ final ContactsProvider2 cp = (ContactsProvider2) getProvider();
+ final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest();
+ mOldMinMatch1 = PhoneNumberUtils.getMinMatchForTest();
+ mOldMinMatch2 = dbHelper.getMinMatchForTest();
+ PhoneNumberUtils.setMinMatchForTest(MIN_MATCH);
+ dbHelper.setMinMatchForTest(MIN_MATCH);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ final ContactsProvider2 cp = (ContactsProvider2) getProvider();
+ final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest();
+ PhoneNumberUtils.setMinMatchForTest(mOldMinMatch1);
+ dbHelper.setMinMatchForTest(mOldMinMatch2);
+ super.tearDown();
+ }
+
public void testConvertEnterpriseUriWithEnterpriseDirectoryToLocalUri() {
String phoneNumber = "886";
String directory = String.valueOf(Directory.ENTERPRISE_DEFAULT);
@@ -3035,16 +3061,16 @@
long rawContactId = RawContactUtil.createRawContactWithBackupId(mResolver, backupId,
account);
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
- // Check if the raw contact is updated.
+ // Check if the raw contact is not updated since Lychee is removed.
assertStoredValue(rawContactUri, RawContacts._ID, rawContactId);
assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, accountType);
assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, accountName);
assertStoredValue(rawContactUri, RawContacts.BACKUP_ID, backupId);
- assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "1");
- assertStoredValue(rawContactUri, RawContacts.STARRED, "1");
- assertStoredValue(rawContactUri, RawContacts.PINNED, "1");
- // Notify metadata network on raw contact insertion
- assertMetadataNetworkNotified(true);
+ assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "0");
+ assertStoredValue(rawContactUri, RawContacts.STARRED, "0");
+ assertStoredValue(rawContactUri, RawContacts.PINNED, "0");
+ // No metadata network notify.
+ assertMetadataNetworkNotified(false);
}
public void testUpdateMetadataOnRawContactBackupIdChange() throws Exception {
@@ -3102,15 +3128,15 @@
ContentValues updatedValues = new ContentValues();
updatedValues.put(RawContacts.BACKUP_ID, backupId);
mResolver.update(RawContacts.CONTENT_URI, updatedValues, null, null);
- // Check if the raw contact is updated because of backup_id change.
+ // Check if the raw contact is still not updated.
assertStoredValue(rawContactUri, RawContacts._ID, rawContactId);
assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, accountType);
assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, accountName);
- assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "1");
- assertStoredValue(rawContactUri, RawContacts.STARRED, "1");
- assertStoredValue(rawContactUri, RawContacts.PINNED, "1");
- // Notify metadata network because of the changed raw contact.
- assertMetadataNetworkNotified(true);
+ assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "0");
+ assertStoredValue(rawContactUri, RawContacts.STARRED, "0");
+ assertStoredValue(rawContactUri, RawContacts.PINNED, "0");
+ // No metadata network notify.
+ assertMetadataNetworkNotified(false);
}
public void testDeleteMetadataOnRawContactDelete() throws Exception {
@@ -3160,12 +3186,12 @@
// Delete raw contact.
mResolver.delete(rawContactUri, null, null);
- // Check if the metadata is deleted.
- assertStoredValue(metadataUri, MetadataSync.DELETED, "1");
+ // Check if the metadata is not deleted.
+ assertStoredValue(metadataUri, MetadataSync.DELETED, "0");
// check raw contact metadata_dirty column is not changed on raw contact deletion
assertMetadataDirty(rawContactUri, false);
- // Notify metadata network on raw contact deletion
- assertMetadataNetworkNotified(true);
+ // Lychee removed. Will not notify it.
+ assertMetadataNetworkNotified(false);
// Add another rawcontact and metadata, and don't delete them.
// Insert a raw contact.
@@ -3189,10 +3215,10 @@
// Check if the metadata is not marked as deleted.
assertStoredValue(metadataUri2, MetadataSync.DELETED, "0");
- // check raw contact metadata_dirty column is changed on raw contact update
- assertMetadataDirty(rawContactUri2, true);
- // Notify metadata network on raw contact update
- assertMetadataNetworkNotified(true);
+ // Will not set metadata_dirty since Lychee is removed.
+ assertMetadataDirty(rawContactUri2, false);
+ // Will not notify Lychee since it's removed.
+ assertMetadataNetworkNotified(false);
}
public void testPostalsQuery() {
@@ -5184,16 +5210,16 @@
updateSendToVoicemailAndRingtone(contactId, true, "foo");
assertSendToVoicemailAndRingtone(contactId, true, "foo");
assertNetworkNotified(false);
- assertMetadataNetworkNotified(true);
+ assertMetadataNetworkNotified(false);
assertDirty(rawContactUri, false);
- assertMetadataDirty(rawContactUri, true);
+ assertMetadataDirty(rawContactUri, false);
updateSendToVoicemailAndRingtoneWithSelection(contactId, false, "bar");
assertSendToVoicemailAndRingtone(contactId, false, "bar");
assertNetworkNotified(false);
- assertMetadataNetworkNotified(true);
+ assertMetadataNetworkNotified(false);
assertDirty(rawContactUri, false);
- assertMetadataDirty(rawContactUri, true);
+ assertMetadataDirty(rawContactUri, false);
}
public void testSendToVoicemailAndRingtoneAfterAggregation() {
@@ -5271,10 +5297,10 @@
assertDirty(rawContactUri1, false);
assertDirty(rawContactUri2, false);
- assertMetadataDirty(rawContactUri1, true);
- assertMetadataDirty(rawContactUri2, true);
+ assertMetadataDirty(rawContactUri1, false);
+ assertMetadataDirty(rawContactUri2, false);
assertNetworkNotified(false);
- assertMetadataNetworkNotified(true);
+ assertMetadataNetworkNotified(false);
}
public void testStatusUpdateInsert() {
@@ -6875,7 +6901,7 @@
assertDirty(rawContactUri, false);
assertMetadataDirty(rawContactUri, false);
assertNetworkNotified(true);
- assertMetadataNetworkNotified(true);
+ assertMetadataNetworkNotified(false);
// When inserting a rawcontact with metadata.
ContentValues values = new ContentValues();
@@ -6884,9 +6910,9 @@
values.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccount.type);
Uri rawContactId2Uri = mResolver.insert(RawContacts.CONTENT_URI, values);
assertDirty(rawContactId2Uri, false);
- assertMetadataDirty(rawContactId2Uri, true);
+ assertMetadataDirty(rawContactId2Uri, false);
assertNetworkNotified(true);
- assertMetadataNetworkNotified(true);
+ assertMetadataNetworkNotified(false);
}
public void testRawContactDirtyAndVersion() {
@@ -6908,8 +6934,8 @@
assertDirty(uri, false);
assertNetworkNotified(false);
- assertMetadataDirty(uri, true);
- assertMetadataNetworkNotified(true);
+ assertMetadataDirty(uri, false);
+ assertMetadataNetworkNotified(false);
Uri emailUri = insertEmail(rawContactId, "goo@woo.com");
assertDirty(uri, true);
@@ -6979,9 +7005,8 @@
long rawContactId = ContentUris.parseId(mResolver.insert(uri, new ContentValues()));
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
assertMetadataDirty(rawContactUri, false);
- // If the raw contact is inserted by sync adapter, it will notify metadata change no matter
- // if there is any metadata change.
- assertMetadataNetworkNotified(true);
+ // Will not notify Lychee since it's removed.
+ assertMetadataNetworkNotified(false);
}
public void testMarkAsMetadataDirtyForRawContactMetadataChange() {
@@ -6999,8 +7024,8 @@
assertStoredValue(contactUri, Contacts.STARRED, 1);
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
- assertMetadataDirty(rawContactUri, true);
- assertMetadataNetworkNotified(true);
+ assertMetadataDirty(rawContactUri, false);
+ assertMetadataNetworkNotified(false);
clearMetadataDirty(rawContactUri);
values = new ContentValues();
@@ -7008,8 +7033,8 @@
mResolver.update(contactUri, values, null, null);
assertStoredValue(contactUri, Contacts.PINNED, 1);
- assertMetadataDirty(rawContactUri, true);
- assertMetadataNetworkNotified(true);
+ assertMetadataDirty(rawContactUri, false);
+ assertMetadataNetworkNotified(false);
clearMetadataDirty(rawContactUri);
values = new ContentValues();
@@ -7017,8 +7042,8 @@
mResolver.update(contactUri, values, null, null);
assertStoredValue(contactUri, Contacts.SEND_TO_VOICEMAIL, 1);
- assertMetadataDirty(rawContactUri, true);
- assertMetadataNetworkNotified(true);
+ assertMetadataDirty(rawContactUri, false);
+ assertMetadataNetworkNotified(false);
}
public void testMarkAsMetadataDirtyForRawContactBackupIdChange() {
@@ -7033,15 +7058,15 @@
ContentValues values = new ContentValues();
values.put(RawContacts.SEND_TO_VOICEMAIL, "1");
mResolver.update(rawContactUri, values, null, null);
- assertMetadataDirty(rawContactUri, true);
+ assertMetadataDirty(rawContactUri, false);
// Update the backup_id and check metadata network should be notified.
values = new ContentValues();
values.put(RawContacts.BACKUP_ID, "newBackupId");
mResolver.update(rawContactUri, values, null, null);
assertStoredValue(rawContactUri, RawContacts.BACKUP_ID, "newBackupId");
- assertMetadataDirty(rawContactUri, true);
- assertMetadataNetworkNotified(true);
+ assertMetadataDirty(rawContactUri, false);
+ assertMetadataNetworkNotified(false);
}
public void testMarkAsMetadataDirtyForAggregationExceptionChange() {
@@ -7056,10 +7081,10 @@
rawContactId1, rawContactId2);
assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1),
- true);
+ false);
assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2),
- true);
- assertMetadataNetworkNotified(true);
+ false);
+ assertMetadataNetworkNotified(false);
}
public void testMarkAsMetadataNotDirtyForUsageStatsChange() {
@@ -7087,8 +7112,8 @@
assertStoredValue(mailUri11, Data.IS_PRIMARY, 1);
assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 1);
assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1),
- true);
- assertMetadataNetworkNotified(true);
+ false);
+ assertMetadataNetworkNotified(false);
}
public void testMarkAsMetadataDirtyForDataPrimarySettingUpdate() {
@@ -7107,8 +7132,8 @@
mResolver.update(mailUri1, values, null, null);
assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
- true);
- assertMetadataNetworkNotified(true);
+ false);
+ assertMetadataNetworkNotified(false);
}
public void testMarkAsMetadataDirtyForDataDelete() {
@@ -7122,8 +7147,8 @@
mResolver.delete(mailUri1, null, null);
assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
- true);
- assertMetadataNetworkNotified(true);
+ false);
+ assertMetadataNetworkNotified(false);
}
public void testDeleteContactWithoutName() {
@@ -7164,6 +7189,98 @@
assertEquals(1, mResolver.delete(lookupUri, null, null));
}
+ public void testDeleteContactComposedOfSingleLocalRawContact() {
+ // Create a raw contact in the local (null) account
+ long rawContactId = RawContactUtil.createRawContact(mResolver, null);
+ DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Smith");
+
+ // Delete the contact
+ long contactId = queryContactId(rawContactId);
+ Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ assertEquals(1, mResolver.delete(contactUri, null, null));
+
+ // Assert that the raw contact was removed
+ Cursor c1 = queryRawContact(rawContactId);
+ assertEquals(0, c1.getCount());
+ c1.close();
+
+ // Assert that the contact was removed
+ Cursor c2 = mResolver.query(contactUri, null, null, null, "");
+ assertEquals(0, c2.getCount());
+ c2.close();
+ }
+
+ public void testDeleteContactComposedOfTwoLocalRawContacts() {
+ // Create a raw contact in the local (null) account
+ long rawContactId1 = RawContactUtil.createRawContact(mResolver, null);
+ DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Smith");
+
+ // Create another local raw contact with the same name
+ long rawContactId2 = RawContactUtil.createRawContact(mResolver, null);
+ DataUtil.insertStructuredName(mResolver, rawContactId2, "John", "Smith");
+
+ // Join the two raw contacts explicitly
+ setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
+ rawContactId1, rawContactId2);
+
+ // Check that the two raw contacts are aggregated together
+ assertAggregated(rawContactId1, rawContactId2, "John Smith");
+
+ // Delete the aggregate contact
+ long contactId = queryContactId(rawContactId1);
+ Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ assertEquals(1, mResolver.delete(contactUri, null, null));
+
+ // Assert that both of the local raw contacts were removed completely
+ Cursor c1 = queryRawContact(rawContactId1);
+ assertEquals(0, c1.getCount());
+ c1.close();
+
+ Cursor c2 = queryRawContact(rawContactId2);
+ assertEquals(0, c2.getCount());
+ c2.close();
+
+ // Assert that the contact was removed
+ Cursor c3 = queryContact(contactId);
+ assertEquals(0, c3.getCount());
+ c3.close();
+ }
+
+ public void testDeleteContactComposedOfSomeLocalRawContacts() {
+ // Create a raw contact in the local (null) account
+ long rawContactId1 = RawContactUtil.createRawContact(mResolver, null);
+ DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Smith");
+
+ // Create another one in a non-local account with the same name
+ long rawContactId2 = RawContactUtil.createRawContact(mResolver, mAccount);
+ DataUtil.insertStructuredName(mResolver, rawContactId2, "John", "Smith");
+
+ // Check that the two new raw contacts are aggregated together
+ assertAggregated(rawContactId1, rawContactId2, "John Smith");
+
+ // Delete the aggregate contact
+ long contactId = queryContactId(rawContactId1);
+ Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ assertEquals(1, mResolver.delete(contactUri, null, null));
+
+ // Assert that the local raw contact was removed completely
+ Cursor c1 = queryRawContact(rawContactId1);
+ assertEquals(0, c1.getCount());
+ c1.close();
+
+ // Assert that the non-local raw contact is still present just marked as deleted
+ Cursor c2 = queryRawContact(rawContactId2);
+ assertEquals(1, c2.getCount());
+ assertTrue(c2.moveToFirst());
+ assertEquals(1, c2.getInt(c2.getColumnIndex(RawContacts.DELETED)));
+ c2.close();
+
+ // Assert that the contact was removed
+ Cursor c3 = queryContact(contactId);
+ assertEquals(0, c3.getCount());
+ c3.close();
+ }
+
public void testQueryContactWithEscapedUri() {
ContentValues values = new ContentValues();
values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~");
@@ -8863,7 +8980,7 @@
}
public void testContactDelete_marksRawContactsForDeletion() {
- DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete();
+ DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete(mAccount);
String[] projection = new String[]{ContactsContract.RawContacts.DIRTY,
ContactsContract.RawContacts.DELETED};
@@ -8877,7 +8994,7 @@
}
public void testContactDelete_checkRawContactContactId() {
- DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete();
+ DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete(mAccount);
String[] projection = new String[]{ContactsContract.RawContacts.CONTACT_ID};
String[] record = RawContactUtil.queryByRawContactId(mResolver, ids.mRawContactId,
@@ -8903,9 +9020,9 @@
ContactUtil.update(mResolver, ids.mContactId, values);
assertDirty(rawContactUri, false);
- assertMetadataDirty(rawContactUri, true);
+ assertMetadataDirty(rawContactUri, false);
assertNetworkNotified(false);
- assertMetadataNetworkNotified(true);
+ assertMetadataNetworkNotified(false);
}
public void testContactUpdate_updatesContactUpdatedTimestamp() {
@@ -9118,13 +9235,23 @@
}
/**
- * Create a contact. Assert it's not present in the delete log. Delete it.
- * And assert that the contact record is no longer present.
+ * Creates a contact in the local account. Assert it's not present in the delete log.
+ * Delete it. And assert that the contact record is no longer present.
*
* @return The contact id and raw contact id that was created.
*/
private DatabaseAsserts.ContactIdPair assertContactCreateDelete() {
- DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
+ return assertContactCreateDelete(null);
+ }
+
+ /**
+ * Creates a contact in the given account. Assert it's not present in the delete log.
+ * Delete it. And assert that the contact record is no longer present.
+ * @return The contact id and raw contact id that was created.
+ */
+ private DatabaseAsserts.ContactIdPair assertContactCreateDelete(Account account) {
+ DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver,
+ account);
assertEquals(CommonDatabaseUtils.NOT_FOUND,
DeletedContactUtil.queryDeletedTimestampForContactId(mResolver, ids.mContactId));
diff --git a/tests/src/com/android/providers/contacts/aggregation/ContactAggregator2Test.java b/tests/src/com/android/providers/contacts/aggregation/ContactAggregator2Test.java
index 927b215..b19a10f 100644
--- a/tests/src/com/android/providers/contacts/aggregation/ContactAggregator2Test.java
+++ b/tests/src/com/android/providers/contacts/aggregation/ContactAggregator2Test.java
@@ -34,10 +34,12 @@
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.StatusUpdates;
+import android.telephony.PhoneNumberUtils;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.MediumTest;
import com.android.providers.contacts.BaseContactsProvider2Test;
+import com.android.providers.contacts.ContactsDatabaseHelper;
import com.android.providers.contacts.ContactsProvider2;
import com.android.providers.contacts.TestUtils;
import com.android.providers.contacts.tests.R;
@@ -69,11 +71,32 @@
AggregationExceptions.RAW_CONTACT_ID2
};
+ private static final int MIN_MATCH = 7;
+
+ private static int mOldMinMatch1;
+ private static int mOldMinMatch2;
+
+ @Override
protected void setUp() throws Exception {
super.setUp();
// Enable new aggregator.
final ContactsProvider2 cp = (ContactsProvider2) getProvider();
cp.setNewAggregatorForTest(true);
+
+ final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest();
+ mOldMinMatch1 = PhoneNumberUtils.getMinMatchForTest();
+ mOldMinMatch2 = dbHelper.getMinMatchForTest();
+ PhoneNumberUtils.setMinMatchForTest(MIN_MATCH);
+ dbHelper.setMinMatchForTest(MIN_MATCH);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ final ContactsProvider2 cp = (ContactsProvider2) getProvider();
+ final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest();
+ PhoneNumberUtils.setMinMatchForTest(mOldMinMatch1);
+ dbHelper.setMinMatchForTest(mOldMinMatch2);
+ super.tearDown();
}
public void testCrudAggregationExceptions() throws Exception {
diff --git a/tests/src/com/android/providers/contacts/aggregation/ContactAggregatorTest.java b/tests/src/com/android/providers/contacts/aggregation/ContactAggregatorTest.java
index fb4f930..56f0883 100644
--- a/tests/src/com/android/providers/contacts/aggregation/ContactAggregatorTest.java
+++ b/tests/src/com/android/providers/contacts/aggregation/ContactAggregatorTest.java
@@ -34,10 +34,12 @@
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.StatusUpdates;
+import android.telephony.PhoneNumberUtils;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.MediumTest;
import com.android.providers.contacts.BaseContactsProvider2Test;
+import com.android.providers.contacts.ContactsDatabaseHelper;
import com.android.providers.contacts.ContactsProvider2;
import com.android.providers.contacts.TestUtils;
import com.android.providers.contacts.tests.R;
@@ -63,17 +65,38 @@
private static final Account ACCOUNT_2 = new Account("account_name_2", "account_type_2");
private static final Account ACCOUNT_3 = new Account("account_name_3", "account_type_3");
+ private static final int MIN_MATCH = 7;
+
+ private int mOldMinMatch1;
+ private int mOldMinMatch2;
+
private static final String[] AGGREGATION_EXCEPTION_PROJECTION = new String[] {
AggregationExceptions.TYPE,
AggregationExceptions.RAW_CONTACT_ID1,
AggregationExceptions.RAW_CONTACT_ID2
};
+ @Override
protected void setUp() throws Exception {
super.setUp();
final ContactsProvider2 cp = (ContactsProvider2) getProvider();
// Make sure to use ContactAggregator.java class
cp.setNewAggregatorForTest(false);
+
+ final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest();
+ mOldMinMatch1 = PhoneNumberUtils.getMinMatchForTest();
+ mOldMinMatch2 = dbHelper.getMinMatchForTest();
+ PhoneNumberUtils.setMinMatchForTest(MIN_MATCH);
+ dbHelper.setMinMatchForTest(MIN_MATCH);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ final ContactsProvider2 cp = (ContactsProvider2) getProvider();
+ final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest();
+ PhoneNumberUtils.setMinMatchForTest(mOldMinMatch1);
+ dbHelper.setMinMatchForTest(mOldMinMatch2);
+ super.tearDown();
}
public void testCrudAggregationExceptions() throws Exception {