CursorToBulkCursorAdaptor.java revision d2183654e03d589b120467f4e98da1b178ceeadb
1/* 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 19import android.os.Bundle; 20import android.os.IBinder; 21import android.os.RemoteException; 22import android.util.Log; 23 24 25/** 26 * Wraps a BulkCursor around an existing Cursor making it remotable. 27 * <p> 28 * If the wrapped cursor is a {@link AbstractWindowedCursor} then it owns 29 * the cursor window. Otherwise, the adaptor takes ownership of the 30 * cursor itself and ensures it gets closed as needed during deactivation 31 * and requeries. 32 * </p> 33 * 34 * {@hide} 35 */ 36public final class CursorToBulkCursorAdaptor extends BulkCursorNative 37 implements IBinder.DeathRecipient { 38 private static final String TAG = "Cursor"; 39 40 private final Object mLock = new Object(); 41 private final String mProviderName; 42 private ContentObserverProxy mObserver; 43 44 /** 45 * The cursor that is being adapted. 46 * This field is set to null when the cursor is closed. 47 */ 48 private CrossProcessCursor mCursor; 49 50 /** 51 * The cursor window used by the cross process cursor. 52 * This field is always null for abstract windowed cursors since they are responsible 53 * for managing the lifetime of their window. 54 */ 55 private CursorWindow mWindowForNonWindowedCursor; 56 57 private static final class ContentObserverProxy extends ContentObserver { 58 protected IContentObserver mRemote; 59 60 public ContentObserverProxy(IContentObserver remoteObserver, DeathRecipient recipient) { 61 super(null); 62 mRemote = remoteObserver; 63 try { 64 remoteObserver.asBinder().linkToDeath(recipient, 0); 65 } catch (RemoteException e) { 66 // Do nothing, the far side is dead 67 } 68 } 69 70 public boolean unlinkToDeath(DeathRecipient recipient) { 71 return mRemote.asBinder().unlinkToDeath(recipient, 0); 72 } 73 74 @Override 75 public boolean deliverSelfNotifications() { 76 // The far side handles the self notifications. 77 return false; 78 } 79 80 @Override 81 public void onChange(boolean selfChange) { 82 try { 83 mRemote.onChange(selfChange); 84 } catch (RemoteException ex) { 85 // Do nothing, the far side is dead 86 } 87 } 88 } 89 90 public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName, 91 CursorWindow window) { 92 try { 93 mCursor = (CrossProcessCursor) cursor; 94 if (mCursor instanceof AbstractWindowedCursor) { 95 AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor; 96 if (windowedCursor.hasWindow()) { 97 if (Log.isLoggable(TAG, Log.VERBOSE) || false) { 98 Log.v(TAG, "Cross process cursor has a local window before setWindow in " 99 + providerName, new RuntimeException()); 100 } 101 } 102 windowedCursor.setWindow(window); // cursor takes ownership of window 103 } else { 104 mWindowForNonWindowedCursor = window; // we own the window 105 mCursor.fillWindow(0, window); 106 } 107 } catch (ClassCastException e) { 108 // TODO Implement this case. 109 window.close(); 110 throw new UnsupportedOperationException( 111 "Only CrossProcessCursor cursors are supported across process for now", e); 112 } 113 mProviderName = providerName; 114 115 synchronized (mLock) { 116 createAndRegisterObserverProxyLocked(observer); 117 } 118 } 119 120 private void closeCursorAndWindowLocked() { 121 if (mCursor != null) { 122 unregisterObserverProxyLocked(); 123 mCursor.close(); 124 mCursor = null; 125 } 126 127 if (mWindowForNonWindowedCursor != null) { 128 mWindowForNonWindowedCursor.close(); 129 mWindowForNonWindowedCursor = null; 130 } 131 } 132 133 private void throwIfCursorIsClosed() { 134 if (mCursor == null) { 135 throw new StaleDataException("Attempted to access a cursor after it has been closed."); 136 } 137 } 138 139 @Override 140 public void binderDied() { 141 synchronized (mLock) { 142 closeCursorAndWindowLocked(); 143 } 144 } 145 146 @Override 147 public CursorWindow getWindow(int startPos) { 148 synchronized (mLock) { 149 throwIfCursorIsClosed(); 150 151 mCursor.moveToPosition(startPos); 152 153 final CursorWindow window; 154 if (mCursor instanceof AbstractWindowedCursor) { 155 window = ((AbstractWindowedCursor)mCursor).getWindow(); 156 } else { 157 window = mWindowForNonWindowedCursor; 158 if (window != null 159 && (startPos < window.getStartPosition() || 160 startPos >= (window.getStartPosition() + window.getNumRows()))) { 161 mCursor.fillWindow(startPos, window); 162 } 163 } 164 165 // Acquire a reference before returning from this RPC. 166 // The Binder proxy will decrement the reference count again as part of writing 167 // the CursorWindow to the reply parcel as a return value. 168 if (window != null) { 169 window.acquireReference(); 170 } 171 return window; 172 } 173 } 174 175 @Override 176 public void onMove(int position) { 177 synchronized (mLock) { 178 throwIfCursorIsClosed(); 179 180 mCursor.onMove(mCursor.getPosition(), position); 181 } 182 } 183 184 @Override 185 public int count() { 186 synchronized (mLock) { 187 throwIfCursorIsClosed(); 188 189 return mCursor.getCount(); 190 } 191 } 192 193 @Override 194 public String[] getColumnNames() { 195 synchronized (mLock) { 196 throwIfCursorIsClosed(); 197 198 return mCursor.getColumnNames(); 199 } 200 } 201 202 @Override 203 public void deactivate() { 204 synchronized (mLock) { 205 if (mCursor != null) { 206 unregisterObserverProxyLocked(); 207 mCursor.deactivate(); 208 } 209 } 210 } 211 212 @Override 213 public void close() { 214 synchronized (mLock) { 215 closeCursorAndWindowLocked(); 216 } 217 } 218 219 @Override 220 public int requery(IContentObserver observer, CursorWindow window) { 221 synchronized (mLock) { 222 throwIfCursorIsClosed(); 223 224 if (mCursor instanceof AbstractWindowedCursor) { 225 ((AbstractWindowedCursor) mCursor).setWindow(window); 226 } else { 227 if (mWindowForNonWindowedCursor != null) { 228 mWindowForNonWindowedCursor.close(); 229 } 230 mWindowForNonWindowedCursor = window; 231 } 232 233 try { 234 if (!mCursor.requery()) { 235 return -1; 236 } 237 } catch (IllegalStateException e) { 238 IllegalStateException leakProgram = new IllegalStateException( 239 mProviderName + " Requery misuse db, mCursor isClosed:" + 240 mCursor.isClosed(), e); 241 throw leakProgram; 242 } 243 244 if (!(mCursor instanceof AbstractWindowedCursor)) { 245 if (window != null) { 246 mCursor.fillWindow(0, window); 247 } 248 } 249 250 unregisterObserverProxyLocked(); 251 createAndRegisterObserverProxyLocked(observer); 252 return mCursor.getCount(); 253 } 254 } 255 256 @Override 257 public boolean getWantsAllOnMoveCalls() { 258 synchronized (mLock) { 259 throwIfCursorIsClosed(); 260 261 return mCursor.getWantsAllOnMoveCalls(); 262 } 263 } 264 265 /** 266 * Create a ContentObserver from the observer and register it as an observer on the 267 * underlying cursor. 268 * @param observer the IContentObserver that wants to monitor the cursor 269 * @throws IllegalStateException if an observer is already registered 270 */ 271 private void createAndRegisterObserverProxyLocked(IContentObserver observer) { 272 if (mObserver != null) { 273 throw new IllegalStateException("an observer is already registered"); 274 } 275 mObserver = new ContentObserverProxy(observer, this); 276 mCursor.registerContentObserver(mObserver); 277 } 278 279 /** Unregister the observer if it is already registered. */ 280 private void unregisterObserverProxyLocked() { 281 if (mObserver != null) { 282 mCursor.unregisterContentObserver(mObserver); 283 mObserver.unlinkToDeath(this); 284 mObserver = null; 285 } 286 } 287 288 @Override 289 public Bundle getExtras() { 290 synchronized (mLock) { 291 throwIfCursorIsClosed(); 292 293 return mCursor.getExtras(); 294 } 295 } 296 297 @Override 298 public Bundle respond(Bundle extras) { 299 synchronized (mLock) { 300 throwIfCursorIsClosed(); 301 302 return mCursor.respond(extras); 303 } 304 } 305} 306