From 5a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0 Mon Sep 17 00:00:00 2001 From: Paul Westbrook Date: Thu, 21 Mar 2013 16:54:49 -0700 Subject: [PATCH] Fix problem with sending attachments in Exchange The problem was that when the attachment was attempted to be opened from the Exchange process, it didn't have access to the cached file. Instead, use a content provider uri to reference the cached file. Bug: 8400456 Change-Id: I80abd66642e938cf09f73bf0e9bd049aa8d7ba1d --- .../emailcommon/internet/Rfc822Output.java | 7 +-- .../emailcommon/provider/EmailContent.java | 22 +++++---- .../android/emailcommon/utility/Utility.java | 8 ++- .../android/email/provider/EmailProvider.java | 49 ++++++++++++++++++- 4 files changed, 70 insertions(+), 16 deletions(-) diff --git a/emailcommon/src/com/android/emailcommon/internet/Rfc822Output.java b/emailcommon/src/com/android/emailcommon/internet/Rfc822Output.java index f77659cef..1a28cef0c 100644 --- a/emailcommon/src/com/android/emailcommon/internet/Rfc822Output.java +++ b/emailcommon/src/com/android/emailcommon/internet/Rfc822Output.java @@ -239,11 +239,12 @@ public class Rfc822Output { inStream = new ByteArrayInputStream(attachment.mContentBytes); } else { // First try the cached file - final String cachedFile = attachment.getCachedFilePath(); + final String cachedFile = attachment.getCachedFileUri(); if (!TextUtils.isEmpty(cachedFile)) { + final Uri cachedFileUri = Uri.parse(cachedFile); try { - inStream = new FileInputStream(cachedFile); - } catch (IOException e) { + inStream = context.getContentResolver().openInputStream(cachedFileUri); + } catch (FileNotFoundException e) { // Couldn't open the cached file, fall back to the original content uri inStream = null; diff --git a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java index 6ea6fa430..d8a9a174d 100755 --- a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java +++ b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java @@ -1196,6 +1196,8 @@ public abstract class EmailContent { public static final String ATTACHMENT_PROVIDER_LEGACY_URI_PREFIX = "content://com.android.email.attachmentprovider"; + public static final String CACHED_FILE_QUERY_PARAM = "filePath"; + public static Uri CONTENT_URI; // This must be used with an appended id: ContentUris.withAppendedId(MESSAGE_ID_URI, id) public static Uri MESSAGE_ID_URI; @@ -1217,7 +1219,7 @@ public abstract class EmailContent { public long mSize; public String mContentId; private String mContentUri; - private String mCachedFile; + private String mCachedFileUri; public long mMessageKey; public String mLocation; public String mEncoding; @@ -1295,12 +1297,12 @@ public abstract class EmailContent { mBaseUri = CONTENT_URI; } - public void setCachedFilePath(String cachedFile) { - mCachedFile = cachedFile; + public void setCachedFileUri(String cachedFile) { + mCachedFileUri = cachedFile; } - public String getCachedFilePath() { - return mCachedFile; + public String getCachedFileUri() { + return mCachedFileUri; } public void setContentUri(String contentUri) { @@ -1404,7 +1406,7 @@ public abstract class EmailContent { mSize = cursor.getLong(CONTENT_SIZE_COLUMN); mContentId = cursor.getString(CONTENT_CONTENT_ID_COLUMN); mContentUri = cursor.getString(CONTENT_CONTENT_URI_COLUMN); - mCachedFile = cursor.getString(CONTENT_CACHED_FILE_COLUMN); + mCachedFileUri = cursor.getString(CONTENT_CACHED_FILE_COLUMN); mMessageKey = cursor.getLong(CONTENT_MESSAGE_ID_COLUMN); mLocation = cursor.getString(CONTENT_LOCATION_COLUMN); mEncoding = cursor.getString(CONTENT_ENCODING_COLUMN); @@ -1425,7 +1427,7 @@ public abstract class EmailContent { values.put(AttachmentColumns.SIZE, mSize); values.put(AttachmentColumns.CONTENT_ID, mContentId); values.put(AttachmentColumns.CONTENT_URI, mContentUri); - values.put(AttachmentColumns.CACHED_FILE, mCachedFile); + values.put(AttachmentColumns.CACHED_FILE, mCachedFileUri); values.put(AttachmentColumns.MESSAGE_KEY, mMessageKey); values.put(AttachmentColumns.LOCATION, mLocation); values.put(AttachmentColumns.ENCODING, mEncoding); @@ -1453,7 +1455,7 @@ public abstract class EmailContent { dest.writeLong(mSize); dest.writeString(mContentId); dest.writeString(mContentUri); - dest.writeString(mCachedFile); + dest.writeString(mCachedFileUri); dest.writeLong(mMessageKey); dest.writeString(mLocation); dest.writeString(mEncoding); @@ -1479,7 +1481,7 @@ public abstract class EmailContent { mSize = in.readLong(); mContentId = in.readString(); mContentUri = in.readString(); - mCachedFile = in.readString(); + mCachedFileUri = in.readString(); mMessageKey = in.readLong(); mLocation = in.readString(); mEncoding = in.readString(); @@ -1514,7 +1516,7 @@ public abstract class EmailContent { @Override public String toString() { return "[" + mFileName + ", " + mMimeType + ", " + mSize + ", " + mContentId + ", " - + mContentUri + ", " + mCachedFile + ", " + mMessageKey + ", " + + mContentUri + ", " + mCachedFileUri + ", " + mMessageKey + ", " + mLocation + ", " + mEncoding + ", " + mFlags + ", " + mContentBytes + ", " + mAccountKey + "," + mUiState + "," + mUiDestination + "," + mUiDownloadedSize + "]"; diff --git a/emailcommon/src/com/android/emailcommon/utility/Utility.java b/emailcommon/src/com/android/emailcommon/utility/Utility.java index 2247e883e..b17fa9a98 100644 --- a/emailcommon/src/com/android/emailcommon/utility/Utility.java +++ b/emailcommon/src/com/android/emailcommon/utility/Utility.java @@ -53,6 +53,7 @@ import com.android.emailcommon.provider.EmailContent.HostAuthColumns; import com.android.emailcommon.provider.EmailContent.Message; import com.android.emailcommon.provider.HostAuth; import com.android.emailcommon.provider.ProviderUnavailableException; +import com.android.mail.utils.LogUtils; import java.io.ByteArrayInputStream; import java.io.File; @@ -745,11 +746,13 @@ public class Utility { } else if (attachment.mContentBytes != null) { return true; } else { - final String cachedFile = attachment.getCachedFilePath(); + final String cachedFile = attachment.getCachedFileUri(); // Try the cached file first if (!TextUtils.isEmpty(cachedFile)) { + final Uri cachedFileUri = Uri.parse(cachedFile); try { - final InputStream inStream = new FileInputStream(cachedFile); + final InputStream inStream = + context.getContentResolver().openInputStream(cachedFileUri); try { inStream.close(); } catch (IOException e) { @@ -758,6 +761,7 @@ public class Utility { return true; } catch (FileNotFoundException e) { // We weren't able to open the file, try the content uri below + LogUtils.e(Logging.LOG_TAG, e, "not able to open cached file"); } } final String contentUri = attachment.getContentUri(); diff --git a/src/com/android/email/provider/EmailProvider.java b/src/com/android/email/provider/EmailProvider.java index 88f9f2bd0..27126021d 100644 --- a/src/com/android/email/provider/EmailProvider.java +++ b/src/com/android/email/provider/EmailProvider.java @@ -37,8 +37,10 @@ import android.database.MergeCursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.net.Uri; +import android.os.Binder; import android.os.Bundle; import android.os.Parcel; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.provider.BaseColumns; import android.text.TextUtils; @@ -101,6 +103,7 @@ import com.google.common.collect.ImmutableSet; import java.io.File; import java.io.FileDescriptor; +import java.io.FileNotFoundException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -199,6 +202,7 @@ public class EmailProvider extends ContentProvider { private static final int ATTACHMENT = ATTACHMENT_BASE; private static final int ATTACHMENT_ID = ATTACHMENT_BASE + 1; private static final int ATTACHMENTS_MESSAGE_ID = ATTACHMENT_BASE + 2; + private static final int ATTACHMENTS_CACHED_FILE_ACCESS = ATTACHMENT_BASE + 3; private static final int HOSTAUTH_BASE = 0x4000; private static final int HOSTAUTH = HOSTAUTH_BASE; @@ -1095,6 +1099,8 @@ public class EmailProvider extends ContentProvider { // The attachments of a specific message (query only) (insert & delete TBD) matcher.addURI(EmailContent.AUTHORITY, "attachment/message/#", ATTACHMENTS_MESSAGE_ID); + matcher.addURI(EmailContent.AUTHORITY, "attachment/cachedFile", + ATTACHMENTS_CACHED_FILE_ACCESS); // All mail bodies matcher.addURI(EmailContent.AUTHORITY, "body", BODY); @@ -1891,6 +1897,39 @@ outer: return result; } + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) { + LogUtils.d(TAG, "EmailProvider.openFile: %s", LogUtils.contentUriToString(TAG, uri)); + } + + final int match = findMatch(uri, "openFile"); + switch (match) { + case ATTACHMENTS_CACHED_FILE_ACCESS: + // Parse the cache file path out from the uri + final String cachedFilePath = + uri.getQueryParameter(EmailContent.Attachment.CACHED_FILE_QUERY_PARAM); + + if (cachedFilePath != null) { + // clearCallingIdentity means that the download manager will + // check our permissions rather than the permissions of whatever + // code is calling us. + long binderToken = Binder.clearCallingIdentity(); + try { + LogUtils.d(TAG, "Opening attachment %s", cachedFilePath); + return ParcelFileDescriptor.open( + new File(cachedFilePath), ParcelFileDescriptor.MODE_READ_ONLY); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + break; + } + + throw new FileNotFoundException("unable to open file"); + } + + /** * Returns the base notification URI for the given content type. * @@ -3473,7 +3512,15 @@ outer: com.android.mail.providers.Attachment uiAtt, String cachedFile) { final Attachment att = new Attachment(); att.setContentUri(uiAtt.contentUri.toString()); - att.setCachedFilePath(cachedFile); + + if (!TextUtils.isEmpty(cachedFile)) { + // Generate the content provider uri for this cached file + final Uri.Builder cachedFileBuilder = Uri.parse( + "content://" + EmailContent.AUTHORITY + "/attachment/cachedFile").buildUpon(); + cachedFileBuilder.appendQueryParameter(Attachment.CACHED_FILE_QUERY_PARAM, cachedFile); + att.setCachedFileUri(cachedFileBuilder.build().toString()); + } + att.mFileName = uiAtt.getName(); att.mMimeType = uiAtt.getContentType(); att.mSize = uiAtt.size;