Apply contact join ops before we reach the max batch op threshold
Splitting the join operations across several batches "could" lead
to some parts of the join being applied and the remaining parts
not applied because of some error.
Our new UX to join more than two contacts also increases the
possibility that the joins take a long time. This is especially
true when a contact involved in the joined is composed of many
raw contacts since the current contact save logic creates an
operation between every pair of raw contacts.
Bug 22480225
Change-Id: Icab836f587808e018bd59909864dfd9de0d53776
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index 9944c77..dc6cdeb 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -142,6 +142,8 @@
private static final int PERSIST_TRIES = 3;
+ private static final int MAX_CONTACTS_PROVIDER_BATCH_SIZE = 499;
+
public interface Listener {
public void onServiceCompleted(Intent callbackIntent);
}
@@ -1078,23 +1080,39 @@
// For each pair of raw contacts, insert an aggregation exception
final ContentResolver resolver = getContentResolver();
- final ArrayList<ContentProviderOperation> operations
- = new ArrayList<ContentProviderOperation>();
+ // The maximum number of operations per batch (aka yield point) is 500. See b/22480225
+ final int batchSize = MAX_CONTACTS_PROVIDER_BATCH_SIZE;
+ final ArrayList<ContentProviderOperation> operations = new ArrayList<>(batchSize);
for (int i = 0; i < rawContactIds.length; i++) {
for (int j = 0; j < rawContactIds.length; j++) {
if (i != j) {
buildJoinContactDiff(operations, rawContactIds[i], rawContactIds[j]);
}
+ // Before we get to 500 we need to flush the operations list
+ if (operations.size() > 0 && operations.size() % batchSize == 0) {
+ if (!applyJoinOperations(resolver, operations)) {
+ return;
+ }
+ operations.clear();
+ }
}
}
+ if (operations.size() > 0 && !applyJoinOperations(resolver, operations)) {
+ return;
+ }
+ showToast(R.string.contactsJoinedMessage);
+ }
- // Apply all aggregation exceptions as one batch
+ /** Returns true if the batch was successfully applied and false otherwise. */
+ private boolean applyJoinOperations(ContentResolver resolver,
+ ArrayList<ContentProviderOperation> operations) {
try {
resolver.applyBatch(ContactsContract.AUTHORITY, operations);
- showToast(R.string.contactsJoinedMessage);
+ return true;
} catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, "Failed to apply aggregation exception batch", e);
showToast(R.string.contactSavedErrorToast);
+ return false;
}
}