Home | History | Annotate | Download | only in database
      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.os.Bundle;
     20 import android.os.IBinder;
     21 import android.os.RemoteException;
     22 import android.util.Log;
     23 
     24 
     25 /**
     26  * Wraps a BulkCursor around an existing Cursor making it remotable.
     27  * <p>
     28  * If the wrapped cursor returns non-null from {@link CrossProcessCursor#getWindow}
     29  * then it is assumed to own the window.  Otherwise, the adaptor provides a
     30  * window to be filled and ensures it gets closed as needed during deactivation
     31  * and requeries.
     32  * </p>
     33  *
     34  * {@hide}
     35  */
     36 public 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 that was filled by the cross process cursor in the
     52      * case where the cursor does not support getWindow.
     53      * This field is only ever non-null when the window has actually be filled.
     54      */
     55     private CursorWindow mFilledWindow;
     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,
     91             String providerName) {
     92         if (cursor instanceof CrossProcessCursor) {
     93             mCursor = (CrossProcessCursor)cursor;
     94         } else {
     95             mCursor = new CrossProcessCursorWrapper(cursor);
     96         }
     97         mProviderName = providerName;
     98 
     99         synchronized (mLock) {
    100             createAndRegisterObserverProxyLocked(observer);
    101         }
    102     }
    103 
    104     private void closeFilledWindowLocked() {
    105         if (mFilledWindow != null) {
    106             mFilledWindow.close();
    107             mFilledWindow = null;
    108         }
    109     }
    110 
    111     private void disposeLocked() {
    112         if (mCursor != null) {
    113             unregisterObserverProxyLocked();
    114             mCursor.close();
    115             mCursor = null;
    116         }
    117 
    118         closeFilledWindowLocked();
    119     }
    120 
    121     private void throwIfCursorIsClosed() {
    122         if (mCursor == null) {
    123             throw new StaleDataException("Attempted to access a cursor after it has been closed.");
    124         }
    125     }
    126 
    127     @Override
    128     public void binderDied() {
    129         synchronized (mLock) {
    130             disposeLocked();
    131         }
    132     }
    133 
    134     @Override
    135     public CursorWindow getWindow(int startPos) {
    136         synchronized (mLock) {
    137             throwIfCursorIsClosed();
    138 
    139             if (!mCursor.moveToPosition(startPos)) {
    140                 closeFilledWindowLocked();
    141                 return null;
    142             }
    143 
    144             CursorWindow window = mCursor.getWindow();
    145             if (window != null) {
    146                 closeFilledWindowLocked();
    147             } else {
    148                 window = mFilledWindow;
    149                 if (window == null) {
    150                     mFilledWindow = new CursorWindow(mProviderName);
    151                     window = mFilledWindow;
    152                     mCursor.fillWindow(startPos, window);
    153                 } else if (startPos < window.getStartPosition()
    154                         || startPos >= window.getStartPosition() + window.getNumRows()) {
    155                     window.clear();
    156                     mCursor.fillWindow(startPos, window);
    157                 }
    158             }
    159 
    160             // Acquire a reference before returning from this RPC.
    161             // The Binder proxy will decrement the reference count again as part of writing
    162             // the CursorWindow to the reply parcel as a return value.
    163             if (window != null) {
    164                 window.acquireReference();
    165             }
    166             return window;
    167         }
    168     }
    169 
    170     @Override
    171     public void onMove(int position) {
    172         synchronized (mLock) {
    173             throwIfCursorIsClosed();
    174 
    175             mCursor.onMove(mCursor.getPosition(), position);
    176         }
    177     }
    178 
    179     @Override
    180     public int count() {
    181         synchronized (mLock) {
    182             throwIfCursorIsClosed();
    183 
    184             return mCursor.getCount();
    185         }
    186     }
    187 
    188     @Override
    189     public String[] getColumnNames() {
    190         synchronized (mLock) {
    191             throwIfCursorIsClosed();
    192 
    193             return mCursor.getColumnNames();
    194         }
    195     }
    196 
    197     @Override
    198     public void deactivate() {
    199         synchronized (mLock) {
    200             if (mCursor != null) {
    201                 unregisterObserverProxyLocked();
    202                 mCursor.deactivate();
    203             }
    204 
    205             closeFilledWindowLocked();
    206         }
    207     }
    208 
    209     @Override
    210     public void close() {
    211         synchronized (mLock) {
    212             disposeLocked();
    213         }
    214     }
    215 
    216     @Override
    217     public int requery(IContentObserver observer) {
    218         synchronized (mLock) {
    219             throwIfCursorIsClosed();
    220 
    221             closeFilledWindowLocked();
    222 
    223             try {
    224                 if (!mCursor.requery()) {
    225                     return -1;
    226                 }
    227             } catch (IllegalStateException e) {
    228                 IllegalStateException leakProgram = new IllegalStateException(
    229                         mProviderName + " Requery misuse db, mCursor isClosed:" +
    230                         mCursor.isClosed(), e);
    231                 throw leakProgram;
    232             }
    233 
    234             unregisterObserverProxyLocked();
    235             createAndRegisterObserverProxyLocked(observer);
    236             return mCursor.getCount();
    237         }
    238     }
    239 
    240     @Override
    241     public boolean getWantsAllOnMoveCalls() {
    242         synchronized (mLock) {
    243             throwIfCursorIsClosed();
    244 
    245             return mCursor.getWantsAllOnMoveCalls();
    246         }
    247     }
    248 
    249     /**
    250      * Create a ContentObserver from the observer and register it as an observer on the
    251      * underlying cursor.
    252      * @param observer the IContentObserver that wants to monitor the cursor
    253      * @throws IllegalStateException if an observer is already registered
    254      */
    255     private void createAndRegisterObserverProxyLocked(IContentObserver observer) {
    256         if (mObserver != null) {
    257             throw new IllegalStateException("an observer is already registered");
    258         }
    259         mObserver = new ContentObserverProxy(observer, this);
    260         mCursor.registerContentObserver(mObserver);
    261     }
    262 
    263     /** Unregister the observer if it is already registered. */
    264     private void unregisterObserverProxyLocked() {
    265         if (mObserver != null) {
    266             mCursor.unregisterContentObserver(mObserver);
    267             mObserver.unlinkToDeath(this);
    268             mObserver = null;
    269         }
    270     }
    271 
    272     @Override
    273     public Bundle getExtras() {
    274         synchronized (mLock) {
    275             throwIfCursorIsClosed();
    276 
    277             return mCursor.getExtras();
    278         }
    279     }
    280 
    281     @Override
    282     public Bundle respond(Bundle extras) {
    283         synchronized (mLock) {
    284             throwIfCursorIsClosed();
    285 
    286             return mCursor.respond(extras);
    287         }
    288     }
    289 }
    290