Fix failure to save a new contact with a photo.
When saving a new contact, we don't know the raw-contact id and
therefore use a "random" negative number. If the new contact has a
photo, we attempt to save it at a bogus path generated from this id.
This manifests as a NullPointerException due to a bug in our
exception-handling code. However, fixing this bug isn't enough
because we still need the photo to save properly.
To achieve this, we check for a negative raw-contact id before saving
the photo. If we detect one, we replace it with the newly-generated
id that we just obtained when we first saved the raw-contact.
Bug: 5937734
Change-Id: Icba2be0e21885fcb418ea9c8c12ebc9820f33bfd
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index 5dd1942..fb549d2 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -60,7 +60,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.Iterator;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -336,6 +335,9 @@
final ContentResolver resolver = getContentResolver();
boolean succeeded = false;
+ // Keep track of the id of a newly raw-contact (if any... there can be at most one).
+ long insertedRawContactId = -1;
+
// Attempt to persist changes
int tries = 0;
while (tries++ < PERSIST_TRIES) {
@@ -358,6 +360,9 @@
if (rawContactId == -1) {
throw new IllegalStateException("Could not determine RawContact ID after save");
}
+ // We don't have to check to see if the value is still -1. If we reach here,
+ // the previous loop iteration didn't succeed, so any ID that we obtained is bogus.
+ insertedRawContactId = getInsertedRawContactId(diff, results);
if (isProfile) {
// Since the profile supports local raw contacts, which may have been completely
// removed if all information was removed, we need to do a special query to
@@ -434,6 +439,17 @@
for (String key : updatedPhotos.keySet()) {
String photoFilePath = updatedPhotos.getString(key);
long rawContactId = Long.parseLong(key);
+
+ // If the raw-contact ID is negative, we are saving a new raw-contact;
+ // replace the bogus ID with the new one that we actually saved the contact at.
+ if (rawContactId < 0) {
+ rawContactId = insertedRawContactId;
+ if (rawContactId == -1) {
+ throw new IllegalStateException(
+ "Could not determine RawContact ID for image insertion");
+ }
+ }
+
File photoFile = new File(photoFilePath);
if (!saveUpdatedPhoto(rawContactId, photoFile)) succeeded = false;
}
@@ -455,50 +471,57 @@
* @return true for success, false for failure
*/
private boolean saveUpdatedPhoto(long rawContactId, File photoFile) {
- Uri outputUri = Uri.withAppendedPath(
+ final Uri outputUri = Uri.withAppendedPath(
ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
RawContacts.DisplayPhoto.CONTENT_DIRECTORY);
- FileOutputStream outputStream = null;
- FileInputStream inputStream = null;
- byte[] buffer = new byte[16 * 1024];
- int length;
- int totalLength = 0;
try {
- AssetFileDescriptor fd = getContentResolver().openAssetFileDescriptor(outputUri, "rw");
- outputStream = fd.createOutputStream();
- inputStream = new FileInputStream(photoFile);
- while ((length = inputStream.read(buffer)) > 0) {
- outputStream.write(buffer, 0, length);
- totalLength += length;
- }
- return true; // yay!
- } catch(IOException e) {
- Log.e(TAG, "Failed to write photo: " + photoFile.toString() + " because: " + e);
- } finally {
- Log.v(TAG, "Wrote " + totalLength + " bytes for photo " + photoFile.toString());
+ final FileOutputStream outputStream = getContentResolver()
+ .openAssetFileDescriptor(outputUri, "rw").createOutputStream();
try {
- inputStream.close();
- } catch(IOException e) {
- Log.e(TAG, "Failed to close photo input stream");
- }
- try {
+ final FileInputStream inputStream = new FileInputStream(photoFile);
+ try {
+ final byte[] buffer = new byte[16 * 1024];
+ int length;
+ int totalLength = 0;
+ while ((length = inputStream.read(buffer)) > 0) {
+ outputStream.write(buffer, 0, length);
+ totalLength += length;
+ }
+ Log.v(TAG, "Wrote " + totalLength + " bytes for photo " + photoFile.toString());
+ } finally {
+ inputStream.close();
+ }
+ } finally {
outputStream.close();
- } catch(IOException e) {
- Log.e(TAG, "Failed to close photo output stream");
}
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write photo: " + photoFile.toString() + " because: " + e);
+ return false;
}
- return false; // failed
+ return true;
}
+ /**
+ * Find the ID of an existing or newly-inserted raw-contact. If none exists, return -1.
+ */
private long getRawContactId(EntityDeltaList state,
final ArrayList<ContentProviderOperation> diff,
final ContentProviderResult[] results) {
- long rawContactId = state.findRawContactId();
- if (rawContactId != -1) {
- return rawContactId;
+ long existingRawContactId = state.findRawContactId();
+ if (existingRawContactId != -1) {
+ return existingRawContactId;
}
+ return getInsertedRawContactId(diff, results);
+ }
+
+ /**
+ * Find the ID of a newly-inserted raw-contact. If none exists, return -1.
+ */
+ private long getInsertedRawContactId(
+ final ArrayList<ContentProviderOperation> diff,
+ final ContentProviderResult[] results) {
final int diffSize = diff.size();
for (int i = 0; i < diffSize; i++) {
ContentProviderOperation operation = diff.get(i);