Merge "Fix cursor window leak when query execution fails."
This commit is contained in:
@@ -78,7 +78,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> {
|
|||||||
// So we treat this case as an unhandled exception.
|
// So we treat this case as an unhandled exception.
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
if (DEBUG) Slog.v(TAG, this + " <<< doInBackground (was canceled)");
|
if (DEBUG) Slog.v(TAG, this + " <<< doInBackground (was canceled)", ex);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,13 +113,21 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
|
|||||||
Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder,
|
Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder,
|
||||||
cancellationSignal);
|
cancellationSignal);
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(
|
try {
|
||||||
cursor, observer, getProviderName());
|
CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(
|
||||||
BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
|
cursor, observer, getProviderName());
|
||||||
|
BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
|
||||||
|
cursor = null;
|
||||||
|
|
||||||
reply.writeNoException();
|
reply.writeNoException();
|
||||||
reply.writeInt(1);
|
reply.writeInt(1);
|
||||||
d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
|
d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
|
||||||
|
} finally {
|
||||||
|
// Close cursor if an exception was thrown while constructing the adaptor.
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
reply.writeNoException();
|
reply.writeNoException();
|
||||||
reply.writeInt(0);
|
reply.writeInt(0);
|
||||||
|
|||||||
@@ -381,6 +381,7 @@ public abstract class ContentResolver {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
IContentProvider stableProvider = null;
|
IContentProvider stableProvider = null;
|
||||||
|
Cursor qCursor = null;
|
||||||
try {
|
try {
|
||||||
long startTime = SystemClock.uptimeMillis();
|
long startTime = SystemClock.uptimeMillis();
|
||||||
|
|
||||||
@@ -390,7 +391,6 @@ public abstract class ContentResolver {
|
|||||||
remoteCancellationSignal = unstableProvider.createCancellationSignal();
|
remoteCancellationSignal = unstableProvider.createCancellationSignal();
|
||||||
cancellationSignal.setRemote(remoteCancellationSignal);
|
cancellationSignal.setRemote(remoteCancellationSignal);
|
||||||
}
|
}
|
||||||
Cursor qCursor;
|
|
||||||
try {
|
try {
|
||||||
qCursor = unstableProvider.query(uri, projection,
|
qCursor = unstableProvider.query(uri, projection,
|
||||||
selection, selectionArgs, sortOrder, remoteCancellationSignal);
|
selection, selectionArgs, sortOrder, remoteCancellationSignal);
|
||||||
@@ -409,20 +409,26 @@ public abstract class ContentResolver {
|
|||||||
if (qCursor == null) {
|
if (qCursor == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// force query execution
|
|
||||||
|
// Force query execution. Might fail and throw a runtime exception here.
|
||||||
qCursor.getCount();
|
qCursor.getCount();
|
||||||
long durationMillis = SystemClock.uptimeMillis() - startTime;
|
long durationMillis = SystemClock.uptimeMillis() - startTime;
|
||||||
maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
|
maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
|
||||||
// Wrap the cursor object into CursorWrapperInner object
|
|
||||||
|
// Wrap the cursor object into CursorWrapperInner object.
|
||||||
CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
|
CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
|
||||||
stableProvider != null ? stableProvider : acquireProvider(uri));
|
stableProvider != null ? stableProvider : acquireProvider(uri));
|
||||||
stableProvider = null;
|
stableProvider = null;
|
||||||
|
qCursor = null;
|
||||||
return wrapper;
|
return wrapper;
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
// Arbitrary and not worth documenting, as Activity
|
// Arbitrary and not worth documenting, as Activity
|
||||||
// Manager will kill this process shortly anyway.
|
// Manager will kill this process shortly anyway.
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
|
if (qCursor != null) {
|
||||||
|
qCursor.close();
|
||||||
|
}
|
||||||
if (unstableProvider != null) {
|
if (unstableProvider != null) {
|
||||||
releaseUnstableProvider(unstableProvider);
|
releaseUnstableProvider(unstableProvider);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,9 +65,14 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> {
|
|||||||
Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
|
Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
|
||||||
mSelectionArgs, mSortOrder, mCancellationSignal);
|
mSelectionArgs, mSortOrder, mCancellationSignal);
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
// Ensure the cursor window is filled
|
try {
|
||||||
cursor.getCount();
|
// Ensure the cursor window is filled.
|
||||||
registerContentObserver(cursor, mObserver);
|
cursor.getCount();
|
||||||
|
registerContentObserver(cursor, mObserver);
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
cursor.close();
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return cursor;
|
return cursor;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -132,6 +132,11 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object that contains sufficient metadata to reconstruct
|
||||||
|
* the cursor remotely. May throw if an error occurs when executing the query
|
||||||
|
* and obtaining the row count.
|
||||||
|
*/
|
||||||
public BulkCursorDescriptor getBulkCursorDescriptor() {
|
public BulkCursorDescriptor getBulkCursorDescriptor() {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
throwIfCursorIsClosed();
|
throwIfCursorIsClosed();
|
||||||
|
|||||||
@@ -138,17 +138,26 @@ public class SQLiteCursor extends AbstractWindowedCursor {
|
|||||||
private void fillWindow(int requiredPos) {
|
private void fillWindow(int requiredPos) {
|
||||||
clearOrCreateWindow(getDatabase().getPath());
|
clearOrCreateWindow(getDatabase().getPath());
|
||||||
|
|
||||||
if (mCount == NO_COUNT) {
|
try {
|
||||||
int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0);
|
if (mCount == NO_COUNT) {
|
||||||
mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true);
|
int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0);
|
||||||
mCursorWindowCapacity = mWindow.getNumRows();
|
mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true);
|
||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
mCursorWindowCapacity = mWindow.getNumRows();
|
||||||
Log.d(TAG, "received count(*) from native_fill_window: " + mCount);
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
|
Log.d(TAG, "received count(*) from native_fill_window: " + mCount);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos,
|
||||||
|
mCursorWindowCapacity);
|
||||||
|
mQuery.fillWindow(mWindow, startPos, requiredPos, false);
|
||||||
}
|
}
|
||||||
} else {
|
} catch (RuntimeException ex) {
|
||||||
int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos,
|
// Close the cursor window if the query failed and therefore will
|
||||||
mCursorWindowCapacity);
|
// not produce any results. This helps to avoid accidentally leaking
|
||||||
mQuery.fillWindow(mWindow, startPos, requiredPos, false);
|
// the cursor window if the client does not correctly handle exceptions
|
||||||
|
// and fails to close the cursor.
|
||||||
|
closeWindow();
|
||||||
|
throw ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user