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 17 package android.database; 18 19 import android.net.Uri; 20 import android.os.Bundle; 21 import android.os.IBinder; 22 import android.os.RemoteException; 23 import android.util.Log; 24 25 26 /** 27 * Wraps a BulkCursor around an existing Cursor making it remotable. 28 * <p> 29 * 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 32 * and requeries. 33 * </p> 34 * 35 * {@hide} 36 */ 37 public final class CursorToBulkCursorAdaptor extends BulkCursorNative 38 implements IBinder.DeathRecipient { 39 private static final String TAG = "Cursor"; 40 41 private final Object mLock = new Object(); 42 private final String mProviderName; 43 private ContentObserverProxy mObserver; 44 45 /** 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 /** 52 * 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. 55 */ 56 private CursorWindow mFilledWindow; 57 58 private static final class ContentObserverProxy extends ContentObserver { 59 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 82 public void onChange(boolean selfChange, Uri uri) { 83 try { 84 mRemote.onChange(selfChange, uri); 85 } catch (RemoteException ex) { 86 // Do nothing, the far side is dead 87 } 88 } 89 } 90 91 public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, 92 String providerName) { 93 if (cursor instanceof CrossProcessCursor) { 94 mCursor = (CrossProcessCursor)cursor; 95 } else { 96 mCursor = new CrossProcessCursorWrapper(cursor); 97 } 98 mProviderName = providerName; 99 100 synchronized (mLock) { 101 createAndRegisterObserverProxyLocked(observer); 102 } 103 } 104 105 private void closeFilledWindowLocked() { 106 if (mFilledWindow != null) { 107 mFilledWindow.close(); 108 mFilledWindow = null; 109 } 110 } 111 112 private void disposeLocked() { 113 if (mCursor != null) { 114 unregisterObserverProxyLocked(); 115 mCursor.close(); 116 mCursor = null; 117 } 118 119 closeFilledWindowLocked(); 120 } 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 129 public void binderDied() { 130 synchronized (mLock) { 131 disposeLocked(); 132 } 133 } 134 135 /** 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 */ 140 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 159 @Override 160 public CursorWindow getWindow(int position) { 161 synchronized (mLock) { 162 throwIfCursorIsClosed(); 163 164 if (!mCursor.moveToPosition(position)) { 165 closeFilledWindowLocked(); 166 return null; 167 } 168 169 CursorWindow window = mCursor.getWindow(); 170 if (window != null) { 171 closeFilledWindowLocked(); 172 } else { 173 window = mFilledWindow; 174 if (window == null) { 175 mFilledWindow = new CursorWindow(mProviderName); 176 window = mFilledWindow; 177 } else if (position < window.getStartPosition() 178 || position >= window.getStartPosition() + window.getNumRows()) { 179 window.clear(); 180 } 181 mCursor.fillWindow(position, window); 182 } 183 184 if (window != null) { 185 // 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. 187 window.acquireReference(); 188 } 189 return window; 190 } 191 } 192 193 @Override 194 public void onMove(int position) { 195 synchronized (mLock) { 196 throwIfCursorIsClosed(); 197 198 mCursor.onMove(mCursor.getPosition(), position); 199 } 200 } 201 202 @Override 203 public void deactivate() { 204 synchronized (mLock) { 205 if (mCursor != null) { 206 unregisterObserverProxyLocked(); 207 mCursor.deactivate(); 208 } 209 210 closeFilledWindowLocked(); 211 } 212 } 213 214 @Override 215 public void close() { 216 synchronized (mLock) { 217 disposeLocked(); 218 } 219 } 220 221 @Override 222 public int requery(IContentObserver observer) { 223 synchronized (mLock) { 224 throwIfCursorIsClosed(); 225 226 closeFilledWindowLocked(); 227 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 239 unregisterObserverProxyLocked(); 240 createAndRegisterObserverProxyLocked(observer); 241 return mCursor.getCount(); 242 } 243 } 244 245 /** 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 */ 251 private void createAndRegisterObserverProxyLocked(IContentObserver observer) { 252 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. */ 260 private void unregisterObserverProxyLocked() { 261 if (mObserver != null) { 262 mCursor.unregisterContentObserver(mObserver); 263 mObserver.unlinkToDeath(this); 264 mObserver = null; 265 } 266 } 267 268 @Override 269 public Bundle getExtras() { 270 synchronized (mLock) { 271 throwIfCursorIsClosed(); 272 273 return mCursor.getExtras(); 274 } 275 } 276 277 @Override 278 public Bundle respond(Bundle extras) { 279 synchronized (mLock) { 280 throwIfCursorIsClosed(); 281 282 return mCursor.respond(extras); 283 } 284 } 285 } 286