summaryrefslogtreecommitdiff
path: root/android/database/CursorToBulkCursorAdaptor.java
diff options
context:
space:
mode:
authorJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
committerJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
commit10d07c88d69cc64f73a069163e7ea5ba2519a099 (patch)
tree8dbd149eb350320a29c3d10e7ad3201de1c5cbee /android/database/CursorToBulkCursorAdaptor.java
parent677516fb6b6f207d373984757d3d9450474b6b00 (diff)
downloadandroid-28-10d07c88d69cc64f73a069163e7ea5ba2519a099.tar.gz
Import Android SDK Platform PI [4335822]
/google/data/ro/projects/android/fetch_artifact \ --bid 4335822 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4335822.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: Ic8f04be005a71c2b9abeaac754d8da8d6f9a2c32
Diffstat (limited to 'android/database/CursorToBulkCursorAdaptor.java')
-rw-r--r--android/database/CursorToBulkCursorAdaptor.java282
1 files changed, 282 insertions, 0 deletions
diff --git a/android/database/CursorToBulkCursorAdaptor.java b/android/database/CursorToBulkCursorAdaptor.java
new file mode 100644
index 00000000..02eddf23
--- /dev/null
+++ b/android/database/CursorToBulkCursorAdaptor.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database;
+
+import android.net.Uri;
+import android.os.*;
+
+
+/**
+ * Wraps a BulkCursor around an existing Cursor making it remotable.
+ * <p>
+ * If the wrapped cursor returns non-null from {@link CrossProcessCursor#getWindow}
+ * then it is assumed to own the window. Otherwise, the adaptor provides a
+ * window to be filled and ensures it gets closed as needed during deactivation
+ * and requeries.
+ * </p>
+ *
+ * {@hide}
+ */
+public final class CursorToBulkCursorAdaptor extends BulkCursorNative
+ implements IBinder.DeathRecipient {
+ private static final String TAG = "Cursor";
+
+ private final Object mLock = new Object();
+ private final String mProviderName;
+ private ContentObserverProxy mObserver;
+
+ /**
+ * The cursor that is being adapted.
+ * This field is set to null when the cursor is closed.
+ */
+ private CrossProcessCursor mCursor;
+
+ /**
+ * The cursor window that was filled by the cross process cursor in the
+ * case where the cursor does not support getWindow.
+ * This field is only ever non-null when the window has actually be filled.
+ */
+ private CursorWindow mFilledWindow;
+
+ private static final class ContentObserverProxy extends ContentObserver {
+ protected IContentObserver mRemote;
+
+ public ContentObserverProxy(IContentObserver remoteObserver, DeathRecipient recipient) {
+ super(null);
+ mRemote = remoteObserver;
+ try {
+ remoteObserver.asBinder().linkToDeath(recipient, 0);
+ } catch (RemoteException e) {
+ // Do nothing, the far side is dead
+ }
+ }
+
+ public boolean unlinkToDeath(DeathRecipient recipient) {
+ return mRemote.asBinder().unlinkToDeath(recipient, 0);
+ }
+
+ @Override
+ public boolean deliverSelfNotifications() {
+ // The far side handles the self notifications.
+ return false;
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ try {
+ mRemote.onChange(selfChange, uri, android.os.Process.myUid());
+ } catch (RemoteException ex) {
+ // Do nothing, the far side is dead
+ }
+ }
+ }
+
+ public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer,
+ String providerName) {
+ if (cursor instanceof CrossProcessCursor) {
+ mCursor = (CrossProcessCursor)cursor;
+ } else {
+ mCursor = new CrossProcessCursorWrapper(cursor);
+ }
+ mProviderName = providerName;
+
+ synchronized (mLock) {
+ createAndRegisterObserverProxyLocked(observer);
+ }
+ }
+
+ private void closeFilledWindowLocked() {
+ if (mFilledWindow != null) {
+ mFilledWindow.close();
+ mFilledWindow = null;
+ }
+ }
+
+ private void disposeLocked() {
+ if (mCursor != null) {
+ unregisterObserverProxyLocked();
+ mCursor.close();
+ mCursor = null;
+ }
+
+ closeFilledWindowLocked();
+ }
+
+ private void throwIfCursorIsClosed() {
+ if (mCursor == null) {
+ throw new StaleDataException("Attempted to access a cursor after it has been closed.");
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ disposeLocked();
+ }
+ }
+
+ /**
+ * 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() {
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ BulkCursorDescriptor d = new BulkCursorDescriptor();
+ d.cursor = this;
+ d.columnNames = mCursor.getColumnNames();
+ d.wantsAllOnMoveCalls = mCursor.getWantsAllOnMoveCalls();
+ d.count = mCursor.getCount();
+ d.window = mCursor.getWindow();
+ if (d.window != null) {
+ // Acquire a reference to the window because its reference count will be
+ // decremented when it is returned as part of the binder call reply parcel.
+ d.window.acquireReference();
+ }
+ return d;
+ }
+ }
+
+ @Override
+ public CursorWindow getWindow(int position) {
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ if (!mCursor.moveToPosition(position)) {
+ closeFilledWindowLocked();
+ return null;
+ }
+
+ CursorWindow window = mCursor.getWindow();
+ if (window != null) {
+ closeFilledWindowLocked();
+ } else {
+ window = mFilledWindow;
+ if (window == null) {
+ mFilledWindow = new CursorWindow(mProviderName);
+ window = mFilledWindow;
+ } else if (position < window.getStartPosition()
+ || position >= window.getStartPosition() + window.getNumRows()) {
+ window.clear();
+ }
+ mCursor.fillWindow(position, window);
+ }
+
+ if (window != null) {
+ // Acquire a reference to the window because its reference count will be
+ // decremented when it is returned as part of the binder call reply parcel.
+ window.acquireReference();
+ }
+ return window;
+ }
+ }
+
+ @Override
+ public void onMove(int position) {
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ mCursor.onMove(mCursor.getPosition(), position);
+ }
+ }
+
+ @Override
+ public void deactivate() {
+ synchronized (mLock) {
+ if (mCursor != null) {
+ unregisterObserverProxyLocked();
+ mCursor.deactivate();
+ }
+
+ closeFilledWindowLocked();
+ }
+ }
+
+ @Override
+ public void close() {
+ synchronized (mLock) {
+ disposeLocked();
+ }
+ }
+
+ @Override
+ public int requery(IContentObserver observer) {
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ closeFilledWindowLocked();
+
+ try {
+ if (!mCursor.requery()) {
+ return -1;
+ }
+ } catch (IllegalStateException e) {
+ IllegalStateException leakProgram = new IllegalStateException(
+ mProviderName + " Requery misuse db, mCursor isClosed:" +
+ mCursor.isClosed(), e);
+ throw leakProgram;
+ }
+
+ unregisterObserverProxyLocked();
+ createAndRegisterObserverProxyLocked(observer);
+ return mCursor.getCount();
+ }
+ }
+
+ /**
+ * Create a ContentObserver from the observer and register it as an observer on the
+ * underlying cursor.
+ * @param observer the IContentObserver that wants to monitor the cursor
+ * @throws IllegalStateException if an observer is already registered
+ */
+ private void createAndRegisterObserverProxyLocked(IContentObserver observer) {
+ if (mObserver != null) {
+ throw new IllegalStateException("an observer is already registered");
+ }
+ mObserver = new ContentObserverProxy(observer, this);
+ mCursor.registerContentObserver(mObserver);
+ }
+
+ /** Unregister the observer if it is already registered. */
+ private void unregisterObserverProxyLocked() {
+ if (mObserver != null) {
+ mCursor.unregisterContentObserver(mObserver);
+ mObserver.unlinkToDeath(this);
+ mObserver = null;
+ }
+ }
+
+ @Override
+ public Bundle getExtras() {
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ return mCursor.getExtras();
+ }
+ }
+
+ @Override
+ public Bundle respond(Bundle extras) {
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ return mCursor.respond(extras);
+ }
+ }
+}