blob: 82a61d4f66a63c03db3dd524c19358e0c4909d4f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.database;
18
Jeff Brown655e66b2012-01-23 15:51:41 -080019import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.os.Bundle;
21import android.os.IBinder;
22import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.util.Log;
24
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025
26/**
27 * Wraps a BulkCursor around an existing Cursor making it remotable.
Jeff Brownd2183652011-10-09 12:39:53 -070028 * <p>
Jeff Brownb4009c72011-10-12 16:05:41 -070029 * If the wrapped cursor returns non-null from {@link CrossProcessCursor#getWindow}
30 * then it is assumed to own the window. Otherwise, the adaptor provides a
31 * window to be filled and ensures it gets closed as needed during deactivation
Jeff Brownd2183652011-10-09 12:39:53 -070032 * and requeries.
33 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034 *
35 * {@hide}
36 */
37public final class CursorToBulkCursorAdaptor extends BulkCursorNative
38 implements IBinder.DeathRecipient {
39 private static final String TAG = "Cursor";
Jeff Brownd2183652011-10-09 12:39:53 -070040
41 private final Object mLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042 private final String mProviderName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043 private ContentObserverProxy mObserver;
44
Jeff Brownd2183652011-10-09 12:39:53 -070045 /**
46 * The cursor that is being adapted.
47 * This field is set to null when the cursor is closed.
48 */
49 private CrossProcessCursor mCursor;
50
51 /**
Jeff Brownb4009c72011-10-12 16:05:41 -070052 * The cursor window that was filled by the cross process cursor in the
53 * case where the cursor does not support getWindow.
54 * This field is only ever non-null when the window has actually be filled.
Jeff Brownd2183652011-10-09 12:39:53 -070055 */
Jeff Brownb4009c72011-10-12 16:05:41 -070056 private CursorWindow mFilledWindow;
Jeff Brownd2183652011-10-09 12:39:53 -070057
58 private static final class ContentObserverProxy extends ContentObserver {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 protected IContentObserver mRemote;
60
61 public ContentObserverProxy(IContentObserver remoteObserver, DeathRecipient recipient) {
62 super(null);
63 mRemote = remoteObserver;
64 try {
65 remoteObserver.asBinder().linkToDeath(recipient, 0);
66 } catch (RemoteException e) {
67 // Do nothing, the far side is dead
68 }
69 }
70
71 public boolean unlinkToDeath(DeathRecipient recipient) {
72 return mRemote.asBinder().unlinkToDeath(recipient, 0);
73 }
74
75 @Override
76 public boolean deliverSelfNotifications() {
77 // The far side handles the self notifications.
78 return false;
79 }
80
81 @Override
Jeff Brown655e66b2012-01-23 15:51:41 -080082 public void onChange(boolean selfChange, Uri uri) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 try {
Jeff Brown655e66b2012-01-23 15:51:41 -080084 mRemote.onChange(selfChange, uri);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 } catch (RemoteException ex) {
86 // Do nothing, the far side is dead
87 }
88 }
89 }
90
Jeff Brown0cde89f2011-10-10 14:50:10 -070091 public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer,
92 String providerName) {
Jeff Brown825c5132011-10-12 16:11:30 -070093 if (cursor instanceof CrossProcessCursor) {
94 mCursor = (CrossProcessCursor)cursor;
95 } else {
96 mCursor = new CrossProcessCursorWrapper(cursor);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 }
98 mProviderName = providerName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099
Jeff Brownd2183652011-10-09 12:39:53 -0700100 synchronized (mLock) {
101 createAndRegisterObserverProxyLocked(observer);
102 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 }
Jeff Brownd2183652011-10-09 12:39:53 -0700104
Jeff Brownb4009c72011-10-12 16:05:41 -0700105 private void closeFilledWindowLocked() {
106 if (mFilledWindow != null) {
107 mFilledWindow.close();
108 mFilledWindow = null;
Jeff Brown0cde89f2011-10-10 14:50:10 -0700109 }
110 }
111
112 private void disposeLocked() {
Jeff Brownd2183652011-10-09 12:39:53 -0700113 if (mCursor != null) {
114 unregisterObserverProxyLocked();
115 mCursor.close();
116 mCursor = null;
117 }
118
Jeff Brownb4009c72011-10-12 16:05:41 -0700119 closeFilledWindowLocked();
Jeff Brownd2183652011-10-09 12:39:53 -0700120 }
121
122 private void throwIfCursorIsClosed() {
123 if (mCursor == null) {
124 throw new StaleDataException("Attempted to access a cursor after it has been closed.");
125 }
126 }
127
128 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 public void binderDied() {
Jeff Brownd2183652011-10-09 12:39:53 -0700130 synchronized (mLock) {
Jeff Brown0cde89f2011-10-10 14:50:10 -0700131 disposeLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 }
133 }
Jeff Brownd2183652011-10-09 12:39:53 -0700134
Jeff Brownc21b5a02013-01-07 17:15:12 -0800135 /**
136 * Returns an object that contains sufficient metadata to reconstruct
137 * the cursor remotely. May throw if an error occurs when executing the query
138 * and obtaining the row count.
139 */
Jeff Brownfb5a4962012-03-14 17:38:59 -0700140 public BulkCursorDescriptor getBulkCursorDescriptor() {
141 synchronized (mLock) {
142 throwIfCursorIsClosed();
143
144 BulkCursorDescriptor d = new BulkCursorDescriptor();
145 d.cursor = this;
146 d.columnNames = mCursor.getColumnNames();
147 d.wantsAllOnMoveCalls = mCursor.getWantsAllOnMoveCalls();
148 d.count = mCursor.getCount();
149 d.window = mCursor.getWindow();
150 if (d.window != null) {
151 // Acquire a reference to the window because its reference count will be
152 // decremented when it is returned as part of the binder call reply parcel.
153 d.window.acquireReference();
154 }
155 return d;
156 }
157 }
158
Jeff Brownd2183652011-10-09 12:39:53 -0700159 @Override
Jeff Brown650de3d2011-10-27 14:52:28 -0700160 public CursorWindow getWindow(int position) {
Jeff Brownd2183652011-10-09 12:39:53 -0700161 synchronized (mLock) {
162 throwIfCursorIsClosed();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163
Jeff Brown650de3d2011-10-27 14:52:28 -0700164 if (!mCursor.moveToPosition(position)) {
Jeff Brownb4009c72011-10-12 16:05:41 -0700165 closeFilledWindowLocked();
166 return null;
167 }
Jeff Brown0cde89f2011-10-10 14:50:10 -0700168
Jeff Brownb4009c72011-10-12 16:05:41 -0700169 CursorWindow window = mCursor.getWindow();
170 if (window != null) {
171 closeFilledWindowLocked();
Jeff Brownd2183652011-10-09 12:39:53 -0700172 } else {
Jeff Brownb4009c72011-10-12 16:05:41 -0700173 window = mFilledWindow;
Jeff Brown0cde89f2011-10-10 14:50:10 -0700174 if (window == null) {
Jeff Brownb4009c72011-10-12 16:05:41 -0700175 mFilledWindow = new CursorWindow(mProviderName);
176 window = mFilledWindow;
Jeff Brown650de3d2011-10-27 14:52:28 -0700177 } else if (position < window.getStartPosition()
178 || position >= window.getStartPosition() + window.getNumRows()) {
Jeff Brownb4009c72011-10-12 16:05:41 -0700179 window.clear();
Jeff Brownd2183652011-10-09 12:39:53 -0700180 }
Jeff Brown650de3d2011-10-27 14:52:28 -0700181 mCursor.fillWindow(position, window);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 }
Jeff Brownd2183652011-10-09 12:39:53 -0700183
Jeff Brownd2183652011-10-09 12:39:53 -0700184 if (window != null) {
Jeff Brownfb5a4962012-03-14 17:38:59 -0700185 // Acquire a reference to the window because its reference count will be
186 // decremented when it is returned as part of the binder call reply parcel.
Jeff Brownd2183652011-10-09 12:39:53 -0700187 window.acquireReference();
188 }
189 return window;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 }
192
Jeff Brownd2183652011-10-09 12:39:53 -0700193 @Override
194 public void onMove(int position) {
195 synchronized (mLock) {
196 throwIfCursorIsClosed();
197
198 mCursor.onMove(mCursor.getPosition(), position);
199 }
200 }
201
202 @Override
Jeff Brownd2183652011-10-09 12:39:53 -0700203 public void deactivate() {
204 synchronized (mLock) {
205 if (mCursor != null) {
206 unregisterObserverProxyLocked();
207 mCursor.deactivate();
208 }
Jeff Brown0cde89f2011-10-10 14:50:10 -0700209
Jeff Brownb4009c72011-10-12 16:05:41 -0700210 closeFilledWindowLocked();
Jeff Brownd2183652011-10-09 12:39:53 -0700211 }
212 }
213
214 @Override
215 public void close() {
216 synchronized (mLock) {
Jeff Brown0cde89f2011-10-10 14:50:10 -0700217 disposeLocked();
Jeff Brownd2183652011-10-09 12:39:53 -0700218 }
219 }
220
221 @Override
Jeff Brown0cde89f2011-10-10 14:50:10 -0700222 public int requery(IContentObserver observer) {
Jeff Brownd2183652011-10-09 12:39:53 -0700223 synchronized (mLock) {
224 throwIfCursorIsClosed();
225
Jeff Brownb4009c72011-10-12 16:05:41 -0700226 closeFilledWindowLocked();
Jeff Brownd2183652011-10-09 12:39:53 -0700227
228 try {
229 if (!mCursor.requery()) {
230 return -1;
231 }
232 } catch (IllegalStateException e) {
233 IllegalStateException leakProgram = new IllegalStateException(
234 mProviderName + " Requery misuse db, mCursor isClosed:" +
235 mCursor.isClosed(), e);
236 throw leakProgram;
237 }
238
Jeff Brownd2183652011-10-09 12:39:53 -0700239 unregisterObserverProxyLocked();
240 createAndRegisterObserverProxyLocked(observer);
241 return mCursor.getCount();
242 }
243 }
244
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 /**
246 * Create a ContentObserver from the observer and register it as an observer on the
247 * underlying cursor.
248 * @param observer the IContentObserver that wants to monitor the cursor
249 * @throws IllegalStateException if an observer is already registered
250 */
Jeff Brownd2183652011-10-09 12:39:53 -0700251 private void createAndRegisterObserverProxyLocked(IContentObserver observer) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 if (mObserver != null) {
253 throw new IllegalStateException("an observer is already registered");
254 }
255 mObserver = new ContentObserverProxy(observer, this);
256 mCursor.registerContentObserver(mObserver);
257 }
258
259 /** Unregister the observer if it is already registered. */
Jeff Brownd2183652011-10-09 12:39:53 -0700260 private void unregisterObserverProxyLocked() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 if (mObserver != null) {
262 mCursor.unregisterContentObserver(mObserver);
263 mObserver.unlinkToDeath(this);
264 mObserver = null;
265 }
266 }
267
Jeff Brownd2183652011-10-09 12:39:53 -0700268 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 public Bundle getExtras() {
Jeff Brownd2183652011-10-09 12:39:53 -0700270 synchronized (mLock) {
271 throwIfCursorIsClosed();
272
273 return mCursor.getExtras();
274 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 }
276
Jeff Brownd2183652011-10-09 12:39:53 -0700277 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 public Bundle respond(Bundle extras) {
Jeff Brownd2183652011-10-09 12:39:53 -0700279 synchronized (mLock) {
280 throwIfCursorIsClosed();
281
282 return mCursor.respond(extras);
283 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 }
285}