* commit '241fe477edba539bcc58df4410c307d9540a4233': Use ashmem for CursorWindows. Bug: 5332296
This commit is contained in:
@@ -108,21 +108,22 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
|
||||
String sortOrder = data.readString();
|
||||
IContentObserver observer = IContentObserver.Stub.asInterface(
|
||||
data.readStrongBinder());
|
||||
CursorWindow window = CursorWindow.CREATOR.createFromParcel(data);
|
||||
|
||||
Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder);
|
||||
if (cursor != null) {
|
||||
CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(
|
||||
cursor, observer, getProviderName(), window);
|
||||
cursor, observer, getProviderName());
|
||||
final IBinder binder = adaptor.asBinder();
|
||||
final int count = adaptor.count();
|
||||
final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex(
|
||||
adaptor.getColumnNames());
|
||||
final boolean wantsAllOnMoveCalls = adaptor.getWantsAllOnMoveCalls();
|
||||
|
||||
reply.writeNoException();
|
||||
reply.writeStrongBinder(binder);
|
||||
reply.writeInt(count);
|
||||
reply.writeInt(index);
|
||||
reply.writeInt(wantsAllOnMoveCalls ? 1 : 0);
|
||||
} else {
|
||||
reply.writeNoException();
|
||||
reply.writeStrongBinder(null);
|
||||
@@ -324,67 +325,58 @@ final class ContentProviderProxy implements IContentProvider
|
||||
|
||||
public Cursor query(Uri url, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) throws RemoteException {
|
||||
CursorWindow window = new CursorWindow(false /* window will be used remotely */);
|
||||
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
try {
|
||||
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
try {
|
||||
data.writeInterfaceToken(IContentProvider.descriptor);
|
||||
data.writeInterfaceToken(IContentProvider.descriptor);
|
||||
|
||||
url.writeToParcel(data, 0);
|
||||
int length = 0;
|
||||
if (projection != null) {
|
||||
length = projection.length;
|
||||
}
|
||||
data.writeInt(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
data.writeString(projection[i]);
|
||||
}
|
||||
data.writeString(selection);
|
||||
if (selectionArgs != null) {
|
||||
length = selectionArgs.length;
|
||||
} else {
|
||||
length = 0;
|
||||
}
|
||||
data.writeInt(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
data.writeString(selectionArgs[i]);
|
||||
}
|
||||
data.writeString(sortOrder);
|
||||
data.writeStrongBinder(adaptor.getObserver().asBinder());
|
||||
window.writeToParcel(data, 0);
|
||||
|
||||
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
IBulkCursor bulkCursor = BulkCursorNative.asInterface(reply.readStrongBinder());
|
||||
if (bulkCursor != null) {
|
||||
int rowCount = reply.readInt();
|
||||
int idColumnPosition = reply.readInt();
|
||||
adaptor.initialize(bulkCursor, rowCount, idColumnPosition);
|
||||
} else {
|
||||
adaptor.close();
|
||||
adaptor = null;
|
||||
}
|
||||
return adaptor;
|
||||
} catch (RemoteException ex) {
|
||||
adaptor.close();
|
||||
throw ex;
|
||||
} catch (RuntimeException ex) {
|
||||
adaptor.close();
|
||||
throw ex;
|
||||
} finally {
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
url.writeToParcel(data, 0);
|
||||
int length = 0;
|
||||
if (projection != null) {
|
||||
length = projection.length;
|
||||
}
|
||||
data.writeInt(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
data.writeString(projection[i]);
|
||||
}
|
||||
data.writeString(selection);
|
||||
if (selectionArgs != null) {
|
||||
length = selectionArgs.length;
|
||||
} else {
|
||||
length = 0;
|
||||
}
|
||||
data.writeInt(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
data.writeString(selectionArgs[i]);
|
||||
}
|
||||
data.writeString(sortOrder);
|
||||
data.writeStrongBinder(adaptor.getObserver().asBinder());
|
||||
|
||||
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
IBulkCursor bulkCursor = BulkCursorNative.asInterface(reply.readStrongBinder());
|
||||
if (bulkCursor != null) {
|
||||
int rowCount = reply.readInt();
|
||||
int idColumnPosition = reply.readInt();
|
||||
boolean wantsAllOnMoveCalls = reply.readInt() != 0;
|
||||
adaptor.initialize(bulkCursor, rowCount, idColumnPosition, wantsAllOnMoveCalls);
|
||||
} else {
|
||||
adaptor.close();
|
||||
adaptor = null;
|
||||
}
|
||||
return adaptor;
|
||||
} catch (RemoteException ex) {
|
||||
adaptor.close();
|
||||
throw ex;
|
||||
} catch (RuntimeException ex) {
|
||||
adaptor.close();
|
||||
throw ex;
|
||||
} finally {
|
||||
// We close the window now because the cursor adaptor does not
|
||||
// take ownership of the window until the first call to onMove.
|
||||
// The adaptor will obtain a fresh reference to the window when
|
||||
// it is filled.
|
||||
window.close();
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,12 +189,14 @@ public abstract class AbstractWindowedCursor extends AbstractCursor {
|
||||
/**
|
||||
* If there is a window, clear it.
|
||||
* Otherwise, creates a local window.
|
||||
*
|
||||
* @param name The window name.
|
||||
* @hide
|
||||
*/
|
||||
protected void clearOrCreateLocalWindow() {
|
||||
protected void clearOrCreateLocalWindow(String name) {
|
||||
if (mWindow == null) {
|
||||
// If there isn't a window set already it will only be accessed locally
|
||||
mWindow = new CursorWindow(true /* the window is local only */);
|
||||
mWindow = new CursorWindow(name, true /* the window is local only */);
|
||||
} else {
|
||||
mWindow.clear();
|
||||
}
|
||||
|
||||
@@ -109,9 +109,8 @@ public abstract class BulkCursorNative extends Binder implements IBulkCursor
|
||||
case REQUERY_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
IContentObserver observer =
|
||||
IContentObserver.Stub.asInterface(data.readStrongBinder());
|
||||
CursorWindow window = CursorWindow.CREATOR.createFromParcel(data);
|
||||
int count = requery(observer, window);
|
||||
IContentObserver.Stub.asInterface(data.readStrongBinder());
|
||||
int count = requery(observer);
|
||||
reply.writeNoException();
|
||||
reply.writeInt(count);
|
||||
reply.writeBundle(getExtras());
|
||||
@@ -294,13 +293,12 @@ final class BulkCursorProxy implements IBulkCursor {
|
||||
}
|
||||
}
|
||||
|
||||
public int requery(IContentObserver observer, CursorWindow window) throws RemoteException {
|
||||
public int requery(IContentObserver observer) throws RemoteException {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
try {
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
data.writeStrongInterface(observer);
|
||||
window.writeToParcel(data, 0);
|
||||
|
||||
boolean result = mRemote.transact(REQUERY_TRANSACTION, data, reply, 0);
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
@@ -38,11 +38,13 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
|
||||
* Initializes the adaptor.
|
||||
* Must be called before first use.
|
||||
*/
|
||||
public void initialize(IBulkCursor bulkCursor, int count, int idIndex) {
|
||||
public void initialize(IBulkCursor bulkCursor, int count, int idIndex,
|
||||
boolean wantsAllOnMoveCalls) {
|
||||
mBulkCursor = bulkCursor;
|
||||
mColumns = null; // lazily retrieved
|
||||
mCount = count;
|
||||
mRowIdColumnIndex = idIndex;
|
||||
mWantsAllOnMoveCalls = wantsAllOnMoveCalls;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,15 +88,12 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
|
||||
|
||||
try {
|
||||
// Make sure we have the proper window
|
||||
if (mWindow != null) {
|
||||
if (newPosition < mWindow.getStartPosition() ||
|
||||
newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
|
||||
setWindow(mBulkCursor.getWindow(newPosition));
|
||||
} else if (mWantsAllOnMoveCalls) {
|
||||
mBulkCursor.onMove(newPosition);
|
||||
}
|
||||
} else {
|
||||
if (mWindow == null
|
||||
|| newPosition < mWindow.getStartPosition()
|
||||
|| newPosition >= mWindow.getStartPosition() + mWindow.getNumRows()) {
|
||||
setWindow(mBulkCursor.getWindow(newPosition));
|
||||
} else if (mWantsAllOnMoveCalls) {
|
||||
mBulkCursor.onMove(newPosition);
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
// We tried to get a window and failed
|
||||
@@ -145,25 +144,19 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
try {
|
||||
CursorWindow newWindow = new CursorWindow(false /* create a remote window */);
|
||||
try {
|
||||
mCount = mBulkCursor.requery(getObserver(), newWindow);
|
||||
if (mCount != -1) {
|
||||
mPos = -1;
|
||||
closeWindow();
|
||||
mCount = mBulkCursor.requery(getObserver());
|
||||
if (mCount != -1) {
|
||||
mPos = -1;
|
||||
closeWindow();
|
||||
|
||||
// super.requery() will call onChanged. Do it here instead of relying on the
|
||||
// observer from the far side so that observers can see a correct value for mCount
|
||||
// when responding to onChanged.
|
||||
super.requery();
|
||||
return true;
|
||||
} else {
|
||||
deactivate();
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
// Don't take ownership of the window until the next call to onMove.
|
||||
newWindow.close();
|
||||
// super.requery() will call onChanged. Do it here instead of relying on the
|
||||
// observer from the far side so that observers can see a correct value for mCount
|
||||
// when responding to onChanged.
|
||||
super.requery();
|
||||
return true;
|
||||
} else {
|
||||
deactivate();
|
||||
return false;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "Unable to requery because the remote process exception " + ex.getMessage());
|
||||
|
||||
@@ -53,6 +53,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
* for managing the lifetime of their window.
|
||||
*/
|
||||
private CursorWindow mWindowForNonWindowedCursor;
|
||||
private boolean mWindowForNonWindowedCursorWasFilled;
|
||||
|
||||
private static final class ContentObserverProxy extends ContentObserver {
|
||||
protected IContentObserver mRemote;
|
||||
@@ -87,26 +88,11 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
}
|
||||
}
|
||||
|
||||
public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName,
|
||||
CursorWindow window) {
|
||||
public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer,
|
||||
String providerName) {
|
||||
try {
|
||||
mCursor = (CrossProcessCursor) cursor;
|
||||
if (mCursor instanceof AbstractWindowedCursor) {
|
||||
AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor;
|
||||
if (windowedCursor.hasWindow()) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE) || false) {
|
||||
Log.v(TAG, "Cross process cursor has a local window before setWindow in "
|
||||
+ providerName, new RuntimeException());
|
||||
}
|
||||
}
|
||||
windowedCursor.setWindow(window); // cursor takes ownership of window
|
||||
} else {
|
||||
mWindowForNonWindowedCursor = window; // we own the window
|
||||
mCursor.fillWindow(0, window);
|
||||
}
|
||||
} catch (ClassCastException e) {
|
||||
// TODO Implement this case.
|
||||
window.close();
|
||||
throw new UnsupportedOperationException(
|
||||
"Only CrossProcessCursor cursors are supported across process for now", e);
|
||||
}
|
||||
@@ -117,17 +103,22 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
}
|
||||
}
|
||||
|
||||
private void closeCursorAndWindowLocked() {
|
||||
private void closeWindowForNonWindowedCursorLocked() {
|
||||
if (mWindowForNonWindowedCursor != null) {
|
||||
mWindowForNonWindowedCursor.close();
|
||||
mWindowForNonWindowedCursor = null;
|
||||
mWindowForNonWindowedCursorWasFilled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void disposeLocked() {
|
||||
if (mCursor != null) {
|
||||
unregisterObserverProxyLocked();
|
||||
mCursor.close();
|
||||
mCursor = null;
|
||||
}
|
||||
|
||||
if (mWindowForNonWindowedCursor != null) {
|
||||
mWindowForNonWindowedCursor.close();
|
||||
mWindowForNonWindowedCursor = null;
|
||||
}
|
||||
closeWindowForNonWindowedCursorLocked();
|
||||
}
|
||||
|
||||
private void throwIfCursorIsClosed() {
|
||||
@@ -139,7 +130,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
@Override
|
||||
public void binderDied() {
|
||||
synchronized (mLock) {
|
||||
closeCursorAndWindowLocked();
|
||||
disposeLocked();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,17 +139,30 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
synchronized (mLock) {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
mCursor.moveToPosition(startPos);
|
||||
|
||||
final CursorWindow window;
|
||||
CursorWindow window;
|
||||
if (mCursor instanceof AbstractWindowedCursor) {
|
||||
window = ((AbstractWindowedCursor)mCursor).getWindow();
|
||||
AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor)mCursor;
|
||||
window = windowedCursor.getWindow();
|
||||
if (window == null) {
|
||||
window = new CursorWindow(mProviderName, false /*localOnly*/);
|
||||
windowedCursor.setWindow(window);
|
||||
}
|
||||
|
||||
mCursor.moveToPosition(startPos);
|
||||
} else {
|
||||
window = mWindowForNonWindowedCursor;
|
||||
if (window != null
|
||||
&& (startPos < window.getStartPosition() ||
|
||||
startPos >= (window.getStartPosition() + window.getNumRows()))) {
|
||||
if (window == null) {
|
||||
window = new CursorWindow(mProviderName, false /*localOnly*/);
|
||||
mWindowForNonWindowedCursor = window;
|
||||
}
|
||||
|
||||
mCursor.moveToPosition(startPos);
|
||||
|
||||
if (!mWindowForNonWindowedCursorWasFilled
|
||||
|| startPos < window.getStartPosition()
|
||||
|| startPos >= window.getStartPosition() + window.getNumRows()) {
|
||||
mCursor.fillWindow(startPos, window);
|
||||
mWindowForNonWindowedCursorWasFilled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,29 +210,24 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
unregisterObserverProxyLocked();
|
||||
mCursor.deactivate();
|
||||
}
|
||||
|
||||
closeWindowForNonWindowedCursorLocked();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (mLock) {
|
||||
closeCursorAndWindowLocked();
|
||||
disposeLocked();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requery(IContentObserver observer, CursorWindow window) {
|
||||
public int requery(IContentObserver observer) {
|
||||
synchronized (mLock) {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
if (mCursor instanceof AbstractWindowedCursor) {
|
||||
((AbstractWindowedCursor) mCursor).setWindow(window);
|
||||
} else {
|
||||
if (mWindowForNonWindowedCursor != null) {
|
||||
mWindowForNonWindowedCursor.close();
|
||||
}
|
||||
mWindowForNonWindowedCursor = window;
|
||||
}
|
||||
closeWindowForNonWindowedCursorLocked();
|
||||
|
||||
try {
|
||||
if (!mCursor.requery()) {
|
||||
@@ -241,12 +240,6 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
throw leakProgram;
|
||||
}
|
||||
|
||||
if (!(mCursor instanceof AbstractWindowedCursor)) {
|
||||
if (window != null) {
|
||||
mCursor.fillWindow(0, window);
|
||||
}
|
||||
}
|
||||
|
||||
unregisterObserverProxyLocked();
|
||||
createAndRegisterObserverProxyLocked(observer);
|
||||
return mCursor.getCount();
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.content.res.Resources;
|
||||
import android.database.sqlite.SQLiteClosable;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.Process;
|
||||
@@ -31,6 +30,13 @@ import android.util.SparseIntArray;
|
||||
|
||||
/**
|
||||
* A buffer containing multiple cursor rows.
|
||||
* <p>
|
||||
* A {@link CursorWindow} is read-write when created and used locally. When sent
|
||||
* to a remote process (by writing it to a {@link Parcel}), the remote process
|
||||
* receives a read-only view of the cursor window. Typically the cursor window
|
||||
* will be allocated by the producer, filled with data, and then sent to the
|
||||
* consumer for reading.
|
||||
* </p>
|
||||
*/
|
||||
public class CursorWindow extends SQLiteClosable implements Parcelable {
|
||||
private static final String STATS_TAG = "CursorWindowStats";
|
||||
@@ -52,10 +58,11 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
|
||||
|
||||
private final CloseGuard mCloseGuard = CloseGuard.get();
|
||||
|
||||
private static native int nativeInitializeEmpty(int cursorWindowSize, boolean localOnly);
|
||||
private static native int nativeInitializeFromBinder(IBinder nativeBinder);
|
||||
private static native int nativeCreate(String name,
|
||||
int cursorWindowSize, boolean localOnly);
|
||||
private static native int nativeCreateFromParcel(Parcel parcel);
|
||||
private static native void nativeDispose(int windowPtr);
|
||||
private static native IBinder nativeGetBinder(int windowPtr);
|
||||
private static native void nativeWriteToParcel(int windowPtr, Parcel parcel);
|
||||
|
||||
private static native void nativeClear(int windowPtr);
|
||||
|
||||
@@ -78,6 +85,30 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
|
||||
private static native boolean nativePutDouble(int windowPtr, double value, int row, int column);
|
||||
private static native boolean nativePutNull(int windowPtr, int row, int column);
|
||||
|
||||
/**
|
||||
* Creates a new empty cursor window and gives it a name.
|
||||
* <p>
|
||||
* The cursor initially has no rows or columns. Call {@link #setNumColumns(int)} to
|
||||
* set the number of columns before adding any rows to the cursor.
|
||||
* </p>
|
||||
*
|
||||
* @param name The name of the cursor window, or null if none.
|
||||
* @param localWindow True if this window will be used in this process only,
|
||||
* false if it might be sent to another processes.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public CursorWindow(String name, boolean localWindow) {
|
||||
mStartPos = 0;
|
||||
mWindowPtr = nativeCreate(name, sCursorWindowSize, localWindow);
|
||||
if (mWindowPtr == 0) {
|
||||
throw new CursorWindowAllocationException("Cursor window allocation of " +
|
||||
(sCursorWindowSize / 1024) + " kb failed. " + printStats());
|
||||
}
|
||||
mCloseGuard.open("close");
|
||||
recordNewWindow(Binder.getCallingPid(), mWindowPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new empty cursor window.
|
||||
* <p>
|
||||
@@ -89,20 +120,12 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
|
||||
* false if it might be sent to another processes.
|
||||
*/
|
||||
public CursorWindow(boolean localWindow) {
|
||||
mStartPos = 0;
|
||||
mWindowPtr = nativeInitializeEmpty(sCursorWindowSize, localWindow);
|
||||
if (mWindowPtr == 0) {
|
||||
throw new CursorWindowAllocationException("Cursor window allocation of " +
|
||||
(sCursorWindowSize / 1024) + " kb failed. " + printStats());
|
||||
}
|
||||
mCloseGuard.open("close");
|
||||
recordNewWindow(Binder.getCallingPid(), mWindowPtr);
|
||||
this(null, localWindow);
|
||||
}
|
||||
|
||||
private CursorWindow(Parcel source) {
|
||||
IBinder binder = source.readStrongBinder();
|
||||
mStartPos = source.readInt();
|
||||
mWindowPtr = nativeInitializeFromBinder(binder);
|
||||
mWindowPtr = nativeCreateFromParcel(source);
|
||||
if (mWindowPtr == 0) {
|
||||
throw new CursorWindowAllocationException("Cursor window could not be "
|
||||
+ "created from binder.");
|
||||
@@ -687,8 +710,8 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeStrongBinder(nativeGetBinder(mWindowPtr));
|
||||
dest.writeInt(mStartPos);
|
||||
nativeWriteToParcel(mWindowPtr, dest);
|
||||
|
||||
if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
|
||||
releaseReference();
|
||||
|
||||
@@ -56,7 +56,7 @@ public interface IBulkCursor extends IInterface {
|
||||
|
||||
public void close() throws RemoteException;
|
||||
|
||||
public int requery(IContentObserver observer, CursorWindow window) throws RemoteException;
|
||||
public int requery(IContentObserver observer) throws RemoteException;
|
||||
|
||||
boolean getWantsAllOnMoveCalls() throws RemoteException;
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ public class SQLiteCursor extends AbstractWindowedCursor {
|
||||
}
|
||||
|
||||
private void fillWindow(int startPos) {
|
||||
clearOrCreateLocalWindow();
|
||||
clearOrCreateLocalWindow(getDatabase().getPath());
|
||||
mWindow.setStartPosition(startPos);
|
||||
int count = getQuery().fillWindow(mWindow);
|
||||
if (startPos == 0) { // fillWindow returns count(*) only for startPos = 0
|
||||
|
||||
Reference in New Issue
Block a user