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.*; 21 22 23 /** 24 * Wraps a BulkCursor around an existing Cursor making it remotable. 25 * <p> 26 * If the wrapped cursor returns non-null from {@link CrossProcessCursor#getWindow} 27 * then it is assumed to own the window. Otherwise, the adaptor provides a 28 * window to be filled and ensures it gets closed as needed during deactivation 29 * and requeries. 30 * </p> 31 * 32 * {@hide} 33 */ 34 public final class CursorToBulkCursorAdaptor extends BulkCursorNative 35 implements IBinder.DeathRecipient { 36 private static final String TAG = "Cursor"; 37 38 private final Object mLock = new Object(); 39 private final String mProviderName; 40 private ContentObserverProxy mObserver; 41 42 /** 43 * The cursor that is being adapted. 44 * This field is set to null when the cursor is closed. 45 */ 46 private CrossProcessCursor mCursor; 47 48 /** 49 * The cursor window that was filled by the cross process cursor in the 50 * case where the cursor does not support getWindow. 51 * This field is only ever non-null when the window has actually be filled. 52 */ 53 private CursorWindow mFilledWindow; 54 55 private static final class ContentObserverProxy extends ContentObserver { 56 protected IContentObserver mRemote; 57 ContentObserverProxy(IContentObserver remoteObserver, DeathRecipient recipient)58 public ContentObserverProxy(IContentObserver remoteObserver, DeathRecipient recipient) { 59 super(null); 60 mRemote = remoteObserver; 61 try { 62 remoteObserver.asBinder().linkToDeath(recipient, 0); 63 } catch (RemoteException e) { 64 // Do nothing, the far side is dead 65 } 66 } 67 unlinkToDeath(DeathRecipient recipient)68 public boolean unlinkToDeath(DeathRecipient recipient) { 69 return mRemote.asBinder().unlinkToDeath(recipient, 0); 70 } 71 72 @Override deliverSelfNotifications()73 public boolean deliverSelfNotifications() { 74 // The far side handles the self notifications. 75 return false; 76 } 77 78 @Override onChange(boolean selfChange, Uri uri)79 public void onChange(boolean selfChange, Uri uri) { 80 try { 81 mRemote.onChange(selfChange, uri, android.os.Process.myUid()); 82 } catch (RemoteException ex) { 83 // Do nothing, the far side is dead 84 } 85 } 86 } 87 CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName)88 public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, 89 String providerName) { 90 if (cursor instanceof CrossProcessCursor) { 91 mCursor = (CrossProcessCursor)cursor; 92 } else { 93 mCursor = new CrossProcessCursorWrapper(cursor); 94 } 95 mProviderName = providerName; 96 97 synchronized (mLock) { 98 createAndRegisterObserverProxyLocked(observer); 99 } 100 } 101 closeFilledWindowLocked()102 private void closeFilledWindowLocked() { 103 if (mFilledWindow != null) { 104 mFilledWindow.close(); 105 mFilledWindow = null; 106 } 107 } 108 disposeLocked()109 private void disposeLocked() { 110 if (mCursor != null) { 111 unregisterObserverProxyLocked(); 112 mCursor.close(); 113 mCursor = null; 114 } 115 116 closeFilledWindowLocked(); 117 } 118 throwIfCursorIsClosed()119 private void throwIfCursorIsClosed() { 120 if (mCursor == null) { 121 throw new StaleDataException("Attempted to access a cursor after it has been closed."); 122 } 123 } 124 125 @Override binderDied()126 public void binderDied() { 127 synchronized (mLock) { 128 disposeLocked(); 129 } 130 } 131 132 /** 133 * Returns an object that contains sufficient metadata to reconstruct 134 * the cursor remotely. May throw if an error occurs when executing the query 135 * and obtaining the row count. 136 */ getBulkCursorDescriptor()137 public BulkCursorDescriptor getBulkCursorDescriptor() { 138 synchronized (mLock) { 139 throwIfCursorIsClosed(); 140 141 BulkCursorDescriptor d = new BulkCursorDescriptor(); 142 d.cursor = this; 143 d.columnNames = mCursor.getColumnNames(); 144 d.wantsAllOnMoveCalls = mCursor.getWantsAllOnMoveCalls(); 145 d.count = mCursor.getCount(); 146 d.window = mCursor.getWindow(); 147 if (d.window != null) { 148 // Acquire a reference to the window because its reference count will be 149 // decremented when it is returned as part of the binder call reply parcel. 150 d.window.acquireReference(); 151 } 152 return d; 153 } 154 } 155 156 @Override getWindow(int position)157 public CursorWindow getWindow(int position) { 158 synchronized (mLock) { 159 throwIfCursorIsClosed(); 160 161 if (!mCursor.moveToPosition(position)) { 162 closeFilledWindowLocked(); 163 return null; 164 } 165 166 CursorWindow window = mCursor.getWindow(); 167 if (window != null) { 168 closeFilledWindowLocked(); 169 } else { 170 window = mFilledWindow; 171 if (window == null) { 172 mFilledWindow = new CursorWindow(mProviderName); 173 window = mFilledWindow; 174 } else if (position < window.getStartPosition() 175 || position >= window.getStartPosition() + window.getNumRows()) { 176 window.clear(); 177 } 178 mCursor.fillWindow(position, window); 179 } 180 181 if (window != null) { 182 // Acquire a reference to the window because its reference count will be 183 // decremented when it is returned as part of the binder call reply parcel. 184 window.acquireReference(); 185 } 186 return window; 187 } 188 } 189 190 @Override onMove(int position)191 public void onMove(int position) { 192 synchronized (mLock) { 193 throwIfCursorIsClosed(); 194 195 mCursor.onMove(mCursor.getPosition(), position); 196 } 197 } 198 199 @Override deactivate()200 public void deactivate() { 201 synchronized (mLock) { 202 if (mCursor != null) { 203 unregisterObserverProxyLocked(); 204 mCursor.deactivate(); 205 } 206 207 closeFilledWindowLocked(); 208 } 209 } 210 211 @Override close()212 public void close() { 213 synchronized (mLock) { 214 disposeLocked(); 215 } 216 } 217 218 @Override requery(IContentObserver observer)219 public int requery(IContentObserver observer) { 220 synchronized (mLock) { 221 throwIfCursorIsClosed(); 222 223 closeFilledWindowLocked(); 224 225 try { 226 if (!mCursor.requery()) { 227 return -1; 228 } 229 } catch (IllegalStateException e) { 230 IllegalStateException leakProgram = new IllegalStateException( 231 mProviderName + " Requery misuse db, mCursor isClosed:" + 232 mCursor.isClosed(), e); 233 throw leakProgram; 234 } 235 236 unregisterObserverProxyLocked(); 237 createAndRegisterObserverProxyLocked(observer); 238 return mCursor.getCount(); 239 } 240 } 241 242 /** 243 * Create a ContentObserver from the observer and register it as an observer on the 244 * underlying cursor. 245 * @param observer the IContentObserver that wants to monitor the cursor 246 * @throws IllegalStateException if an observer is already registered 247 */ createAndRegisterObserverProxyLocked(IContentObserver observer)248 private void createAndRegisterObserverProxyLocked(IContentObserver observer) { 249 if (mObserver != null) { 250 throw new IllegalStateException("an observer is already registered"); 251 } 252 mObserver = new ContentObserverProxy(observer, this); 253 mCursor.registerContentObserver(mObserver); 254 } 255 256 /** Unregister the observer if it is already registered. */ unregisterObserverProxyLocked()257 private void unregisterObserverProxyLocked() { 258 if (mObserver != null) { 259 mCursor.unregisterContentObserver(mObserver); 260 mObserver.unlinkToDeath(this); 261 mObserver = null; 262 } 263 } 264 265 @Override getExtras()266 public Bundle getExtras() { 267 synchronized (mLock) { 268 throwIfCursorIsClosed(); 269 270 return mCursor.getExtras(); 271 } 272 } 273 274 @Override respond(Bundle extras)275 public Bundle respond(Bundle extras) { 276 synchronized (mLock) { 277 throwIfCursorIsClosed(); 278 279 return mCursor.respond(extras); 280 } 281 } 282 } 283