Bypass the cursor completely when passing message within EmailContent contexts
We should investigate using PFDs when passing to the UI layer too, for consistency Change-Id: I21ad1987926b93af20287ae8e49bfc7a4ad99570
This commit is contained in:
parent
1b8cb007ba
commit
9caaebb142
@ -28,9 +28,12 @@ import android.database.ContentObservable;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.Looper;
|
||||
import android.os.Parcel;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.BaseColumns;
|
||||
@ -43,7 +46,11 @@ import com.android.mail.providers.UIProvider;
|
||||
import com.android.mail.utils.LogUtils;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@ -471,6 +478,16 @@ public abstract class EmailContent {
|
||||
public static final int CONTENT_INTRO_TEXT_COLUMN = 7;
|
||||
public static final int CONTENT_QUOTED_TEXT_START_POS_COLUMN = 8;
|
||||
|
||||
/**
|
||||
* Following values are for EmailMessageCursor
|
||||
*/
|
||||
// Value is an int specifying the row to get
|
||||
public static final String RESPOND_COMMAND_GET_HTML_PIPE = "EmailMessageCursor.getHtmlPipe";
|
||||
public static final String RESPOND_COMMAND_GET_TEXT_PIPE = "EmailMessageCursor.getTextPipe";
|
||||
// Value returned is a ParcelFileDescriptor pipe, or null if no content is present
|
||||
public static final String RESPOND_RESULT_HTML_PIPE_KEY = "EmailMessageCursor.htmlPipe";
|
||||
public static final String RESPOND_RESULT_TEXT_PIPE_KEY = "EmailMessageCursor.textPipe";
|
||||
|
||||
public static final String[] CONTENT_PROJECTION = new String[] {
|
||||
BodyColumns._ID,
|
||||
BodyColumns.MESSAGE_KEY,
|
||||
@ -651,12 +668,48 @@ public abstract class EmailContent {
|
||||
return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_INTRO);
|
||||
}
|
||||
|
||||
private static String readBodyFromPipe(ParcelFileDescriptor d) {
|
||||
final AutoCloseInputStream htmlInput = new AutoCloseInputStream(d);
|
||||
String content = null;
|
||||
try {
|
||||
content = IOUtils.toString(htmlInput, "utf8");
|
||||
} catch (final IOException e) {
|
||||
LogUtils.e(LogUtils.TAG, e, "IOError while reading message body");
|
||||
content = null;
|
||||
} finally {
|
||||
try {
|
||||
htmlInput.close();
|
||||
} catch (final IOException e) {
|
||||
LogUtils.e(LogUtils.TAG, e, "IOError while closing message body");
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restore(Cursor cursor) {
|
||||
mBaseUri = EmailContent.Body.CONTENT_URI;
|
||||
mMessageKey = cursor.getLong(CONTENT_MESSAGE_KEY_COLUMN);
|
||||
// These get overwritten below if we find a file descriptor in the respond() call,
|
||||
// but we'll keep this here in case we want to construct a matrix cursor or something
|
||||
// to build a Body object from.
|
||||
mHtmlContent = cursor.getString(CONTENT_HTML_CONTENT_COLUMN);
|
||||
mTextContent = cursor.getString(CONTENT_TEXT_CONTENT_COLUMN);
|
||||
final int rowId = cursor.getPosition();
|
||||
final Bundle command = new Bundle(2);
|
||||
command.putInt(RESPOND_COMMAND_GET_HTML_PIPE, rowId);
|
||||
command.putInt(RESPOND_COMMAND_GET_TEXT_PIPE, rowId);
|
||||
final Bundle response = cursor.respond(command);
|
||||
final ParcelFileDescriptor htmlDescriptor =
|
||||
response.getParcelable(RESPOND_RESULT_HTML_PIPE_KEY);
|
||||
if (htmlDescriptor != null) {
|
||||
mHtmlContent = readBodyFromPipe(htmlDescriptor);
|
||||
}
|
||||
final ParcelFileDescriptor textDescriptor =
|
||||
response.getParcelable(RESPOND_RESULT_TEXT_PIPE_KEY);
|
||||
if (textDescriptor != null) {
|
||||
mTextContent = readBodyFromPipe(textDescriptor);
|
||||
}
|
||||
mHtmlReply = cursor.getString(CONTENT_HTML_REPLY_COLUMN);
|
||||
mTextReply = cursor.getString(CONTENT_TEXT_REPLY_COLUMN);
|
||||
mSourceKey = cursor.getLong(CONTENT_SOURCE_KEY_COLUMN);
|
||||
|
@ -21,13 +21,20 @@ import android.database.CursorWrapper;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteDoneException;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
|
||||
import android.provider.BaseColumns;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.emailcommon.provider.EmailContent.Body;
|
||||
import com.android.emailcommon.provider.EmailContent.BodyColumns;
|
||||
import com.android.mail.utils.LogUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class wraps a cursor for the purpose of bypassing the CursorWindow object for the
|
||||
* potentially over-sized body content fields. The CursorWindow has a hard limit of 2MB and so a
|
||||
@ -50,10 +57,12 @@ public class EmailMessageCursor extends CursorWrapper {
|
||||
private final SparseArray<String> mHtmlParts;
|
||||
private final int mTextColumnIndex;
|
||||
private final int mHtmlColumnIndex;
|
||||
private final boolean mDeliverColumnsInline;
|
||||
|
||||
public EmailMessageCursor(final Cursor cursor, final SQLiteDatabase db, final String htmlColumn,
|
||||
final String textColumn) {
|
||||
final String textColumn, final boolean deliverColumnsInline) {
|
||||
super(cursor);
|
||||
mDeliverColumnsInline = deliverColumnsInline;
|
||||
mHtmlColumnIndex = cursor.getColumnIndex(htmlColumn);
|
||||
mTextColumnIndex = cursor.getColumnIndex(textColumn);
|
||||
final int cursorSize = cursor.getCount();
|
||||
@ -94,11 +103,13 @@ public class EmailMessageCursor extends CursorWrapper {
|
||||
|
||||
@Override
|
||||
public String getString(final int columnIndex) {
|
||||
if (mDeliverColumnsInline) {
|
||||
if (columnIndex == mHtmlColumnIndex) {
|
||||
return mHtmlParts.get(getPosition());
|
||||
} else if (columnIndex == mTextColumnIndex) {
|
||||
return mTextParts.get(getPosition());
|
||||
}
|
||||
}
|
||||
return super.getString(columnIndex);
|
||||
}
|
||||
|
||||
@ -112,4 +123,53 @@ public class EmailMessageCursor extends CursorWrapper {
|
||||
return super.getType(columnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private static ParcelFileDescriptor createPipeAndFillAsync(final String contents) {
|
||||
try {
|
||||
final ParcelFileDescriptor descriptors[] = ParcelFileDescriptor.createPipe();
|
||||
final ParcelFileDescriptor readDescriptor = descriptors[0];
|
||||
final ParcelFileDescriptor writeDescriptor = descriptors[1];
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
final AutoCloseOutputStream outStream =
|
||||
new AutoCloseOutputStream(writeDescriptor);
|
||||
try {
|
||||
outStream.write(contents.getBytes("utf8"));
|
||||
} catch (final IOException e) {
|
||||
LogUtils.e(LogUtils.TAG, e, "IOException while writing to body pipe");
|
||||
} finally {
|
||||
try {
|
||||
outStream.close();
|
||||
} catch (final IOException e) {
|
||||
LogUtils.e(LogUtils.TAG, e, "IOException while closing body pipe");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
return readDescriptor;
|
||||
} catch (final IOException e) {
|
||||
LogUtils.e(LogUtils.TAG, e, "IOException while creating body pipe");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle respond(Bundle extras) {
|
||||
final int htmlRow = extras.getInt(Body.RESPOND_COMMAND_GET_HTML_PIPE, -1);
|
||||
final int textRow = extras.getInt(Body.RESPOND_COMMAND_GET_TEXT_PIPE, -1);
|
||||
|
||||
final Bundle b = new Bundle(2);
|
||||
|
||||
if (htmlRow >= 0 && !TextUtils.isEmpty(mHtmlParts.get(htmlRow))) {
|
||||
b.putParcelable(Body.RESPOND_RESULT_HTML_PIPE_KEY,
|
||||
createPipeAndFillAsync(mHtmlParts.get(htmlRow)));
|
||||
}
|
||||
if (textRow >= 0 && !TextUtils.isEmpty(mTextParts.get(textRow))) {
|
||||
b.putParcelable(Body.RESPOND_RESULT_TEXT_PIPE_KEY,
|
||||
createPipeAndFillAsync(mTextParts.get(textRow)));
|
||||
}
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
@ -1312,8 +1312,11 @@ public class EmailProvider extends ContentProvider {
|
||||
}
|
||||
c = db.rawQuery(sb.toString(), selectionArgs);
|
||||
if (c != null) {
|
||||
// We don't want to deliver the body contents inline here because we might
|
||||
// be sending this cursor to the Exchange process, and we'll blow out the
|
||||
// CursorWindow if there's a large message body.
|
||||
c = new EmailMessageCursor(c, db, BodyColumns.HTML_CONTENT,
|
||||
BodyColumns.TEXT_CONTENT);
|
||||
BodyColumns.TEXT_CONTENT, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2383,8 +2386,8 @@ public class EmailProvider extends ContentProvider {
|
||||
.add(UIProvider.MessageColumns.BCC, MessageColumns.BCC_LIST)
|
||||
.add(UIProvider.MessageColumns.REPLY_TO, MessageColumns.REPLY_TO_LIST)
|
||||
.add(UIProvider.MessageColumns.DATE_RECEIVED_MS, MessageColumns.TIMESTAMP)
|
||||
.add(UIProvider.MessageColumns.BODY_HTML, "") // Loaded in EmailMessageCursor
|
||||
.add(UIProvider.MessageColumns.BODY_TEXT, "") // Loaded in EmailMessageCursor
|
||||
.add(UIProvider.MessageColumns.BODY_HTML, null) // Loaded in EmailMessageCursor
|
||||
.add(UIProvider.MessageColumns.BODY_TEXT, null) // Loaded in EmailMessageCursor
|
||||
.add(UIProvider.MessageColumns.REF_MESSAGE_ID, "0")
|
||||
.add(UIProvider.MessageColumns.DRAFT_TYPE, NOT_A_DRAFT_STRING)
|
||||
.add(UIProvider.MessageColumns.APPEND_REF_MESSAGE_CONTENT, "0")
|
||||
@ -4296,7 +4299,7 @@ public class EmailProvider extends ContentProvider {
|
||||
}
|
||||
if (c != null) {
|
||||
c = new EmailMessageCursor(c, db, UIProvider.MessageColumns.BODY_HTML,
|
||||
UIProvider.MessageColumns.BODY_TEXT);
|
||||
UIProvider.MessageColumns.BODY_TEXT, true /* deliverColumnsInline */);
|
||||
}
|
||||
notifyUri = UIPROVIDER_MESSAGE_NOTIFIER.buildUpon().appendPath(id).build();
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user