private Builder buildAssertHelper() {
   final boolean isContactInsert = mValues.isInsert();
   ContentProviderOperation.Builder builder = null;
   if (!isContactInsert) {
     // Assert version is consistent while persisting changes
     final Long beforeId = mValues.getId();
     final Long beforeVersion = mValues.getAsLong(RawContacts.VERSION);
     if (beforeId == null || beforeVersion == null) return builder;
     builder = ContentProviderOperation.newAssertQuery(mContactsQueryUri);
     builder.withSelection(RawContacts._ID + "=" + beforeId, null);
     builder.withValue(RawContacts.VERSION, beforeVersion);
   }
   return builder;
 }
  /** Find entry with the given {@link BaseColumns#_ID} value. */
  public ValuesDelta getEntry(Long childId) {
    if (childId == null) {
      // Requesting an "insert" entry, which has no "before"
      return null;
    }

    // Search all children for requested entry
    for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
      for (ValuesDelta entry : mimeEntries) {
        if (childId.equals(entry.getId())) {
          return entry;
        }
      }
    }
    return null;
  }
  private static String getMapKey(
      RawContactDelta entity, DataKind kind, ValuesDelta values, int viewIndex) {
    sWorkStringBuilder.setLength(0);
    if (entity != null) {
      sWorkStringBuilder.append(entity.getValues().getId());

      if (kind != null) {
        sWorkStringBuilder.append(KEY_SEPARATOR);
        sWorkStringBuilder.append(kind.mimeType);

        if (values != null) {
          sWorkStringBuilder.append(KEY_SEPARATOR);
          sWorkStringBuilder.append(values.getId());

          if (viewIndex != NO_VIEW_INDEX) {
            sWorkStringBuilder.append(KEY_SEPARATOR);
            sWorkStringBuilder.append(viewIndex);
          }
        }
      }
    }
    return sWorkStringBuilder.toString();
  }
  /**
   * Merge the "after" values from the given {@link RawContactDelta} onto the "before" state
   * represented by this {@link RawContactDelta}, discarding any existing "after" states. This is
   * typically used when re-parenting changes onto an updated {@link Entity}.
   */
  public static RawContactDelta mergeAfter(RawContactDelta local, RawContactDelta remote) {
    // Bail early if trying to merge delete with missing local
    final ValuesDelta remoteValues = remote.mValues;
    if (local == null && (remoteValues.isDelete() || remoteValues.isTransient())) return null;

    // Create local version if none exists yet
    if (local == null) local = new RawContactDelta();

    if (LOGV) {
      final Long localVersion =
          (local.mValues == null) ? null : local.mValues.getAsLong(RawContacts.VERSION);
      final Long remoteVersion = remote.mValues.getAsLong(RawContacts.VERSION);
      Log.d(TAG, "Re-parenting from original version " + remoteVersion + " to " + localVersion);
    }

    // Create values if needed, and merge "after" changes
    local.mValues = ValuesDelta.mergeAfter(local.mValues, remote.mValues);

    // Find matching local entry for each remote values, or create
    for (ArrayList<ValuesDelta> mimeEntries : remote.mEntries.values()) {
      for (ValuesDelta remoteEntry : mimeEntries) {
        final Long childId = remoteEntry.getId();

        // Find or create local match and merge
        final ValuesDelta localEntry = local.getEntry(childId);
        final ValuesDelta merged = ValuesDelta.mergeAfter(localEntry, remoteEntry);

        if (localEntry == null && merged != null) {
          // No local entry before, so insert
          local.addEntry(merged);
        }
      }
    }

    return local;
  }
  /**
   * For compatibility purpose, this method is copied from {@link #buildDiff} and takes an ArrayList
   * of CPOWrapper as parameter.
   */
  public void buildDiffWrapper(ArrayList<CPOWrapper> buildInto) {
    final int firstIndex = buildInto.size();

    final boolean isContactInsert = mValues.isInsert();
    final boolean isContactDelete = mValues.isDelete();
    final boolean isContactUpdate = !isContactInsert && !isContactDelete;

    final Long beforeId = mValues.getId();

    if (isContactInsert) {
      // TODO: for now simply disabling aggregation when a new contact is
      // created on the phone.  In the future, will show aggregation suggestions
      // after saving the contact.
      mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
    }

    // Build possible operation at Contact level
    BuilderWrapper bw = mValues.buildDiffWrapper(mContactsQueryUri);
    possibleAddWrapper(buildInto, bw);

    // Build operations for all children
    for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
      for (ValuesDelta child : mimeEntries) {
        // Ignore children if parent was deleted
        if (isContactDelete) continue;

        // Use the profile data URI if the contact is the profile.
        if (mContactsQueryUri.equals(Profile.CONTENT_RAW_CONTACTS_URI)) {
          bw =
              child.buildDiffWrapper(
                  Uri.withAppendedPath(Profile.CONTENT_URI, RawContacts.Data.CONTENT_DIRECTORY));
        } else {
          bw = child.buildDiffWrapper(Data.CONTENT_URI);
        }

        if (child.isInsert()) {
          if (isContactInsert) {
            // Parent is brand new insert, so back-reference _id
            bw.getBuilder().withValueBackReference(Data.RAW_CONTACT_ID, firstIndex);
          } else {
            // Inserting under existing, so fill with known _id
            bw.getBuilder().withValue(Data.RAW_CONTACT_ID, beforeId);
          }
        } else if (isContactInsert && bw != null && bw.getBuilder() != null) {
          // Child must be insert when Contact insert
          throw new IllegalArgumentException("When parent insert, child must be also");
        }
        possibleAddWrapper(buildInto, bw);
      }
    }

    final boolean addedOperations = buildInto.size() > firstIndex;
    if (addedOperations && isContactUpdate) {
      // Suspend aggregation while persisting updates
      Builder builder = buildSetAggregationMode(beforeId, RawContacts.AGGREGATION_MODE_SUSPENDED);
      buildInto.add(firstIndex, new CPOWrapper(builder.build(), CompatUtils.TYPE_UPDATE));

      // Restore aggregation mode as last operation
      builder = buildSetAggregationMode(beforeId, RawContacts.AGGREGATION_MODE_DEFAULT);
      buildInto.add(new CPOWrapper(builder.build(), CompatUtils.TYPE_UPDATE));
    } else if (isContactInsert) {
      // Restore aggregation mode as last operation
      Builder builder = ContentProviderOperation.newUpdate(mContactsQueryUri);
      builder.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT);
      builder.withSelection(RawContacts._ID + "=?", new String[1]);
      builder.withSelectionBackReference(0, firstIndex);
      buildInto.add(new CPOWrapper(builder.build(), CompatUtils.TYPE_UPDATE));
    }
  }