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.database.sqlite.SQLiteMisuseException;
     20 import android.os.Binder;
     21 import android.os.Bundle;
     22 import android.os.IBinder;
     23 import android.os.RemoteException;
     24 import android.util.Config;
     25 import android.util.Log;
     26 
     27 import java.util.Map;
     28 
     29 
     30 /**
     31  * Wraps a BulkCursor around an existing Cursor making it remotable.
     32  *
     33  * {@hide}
     34  */
     35 public final class CursorToBulkCursorAdaptor extends BulkCursorNative
     36         implements IBinder.DeathRecipient {
     37     private static final String TAG = "Cursor";
     38     private final CrossProcessCursor mCursor;
     39     private CursorWindow mWindow;
     40     private final String mProviderName;
     41     private final boolean mReadOnly;
     42     private ContentObserverProxy mObserver;
     43 
     44     private static final class ContentObserverProxy extends ContentObserver
     45             {
     46         protected IContentObserver mRemote;
     47 
     48         public ContentObserverProxy(IContentObserver remoteObserver, DeathRecipient recipient) {
     49             super(null);
     50             mRemote = remoteObserver;
     51             try {
     52                 remoteObserver.asBinder().linkToDeath(recipient, 0);
     53             } catch (RemoteException e) {
     54                 // Do nothing, the far side is dead
     55             }
     56         }
     57 
     58         public boolean unlinkToDeath(DeathRecipient recipient) {
     59             return mRemote.asBinder().unlinkToDeath(recipient, 0);
     60         }
     61 
     62         @Override
     63         public boolean deliverSelfNotifications() {
     64             // The far side handles the self notifications.
     65             return false;
     66         }
     67 
     68         @Override
     69         public void onChange(boolean selfChange) {
     70             try {
     71                 mRemote.onChange(selfChange);
     72             } catch (RemoteException ex) {
     73                 // Do nothing, the far side is dead
     74             }
     75         }
     76     }
     77 
     78     public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName,
     79             boolean allowWrite, CursorWindow window) {
     80         try {
     81             mCursor = (CrossProcessCursor) cursor;
     82             if (mCursor instanceof AbstractWindowedCursor) {
     83                 AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor;
     84                 if (windowedCursor.hasWindow()) {
     85                     if (Log.isLoggable(TAG, Log.VERBOSE) || Config.LOGV) {
     86                         Log.v(TAG, "Cross process cursor has a local window before setWindow in "
     87                                 + providerName, new RuntimeException());
     88                     }
     89                 }
     90                 windowedCursor.setWindow(window);
     91             } else {
     92                 mWindow = window;
     93                 mCursor.fillWindow(0, window);
     94             }
     95         } catch (ClassCastException e) {
     96             // TODO Implement this case.
     97             throw new UnsupportedOperationException(
     98                     "Only CrossProcessCursor cursors are supported across process for now", e);
     99         }
    100         mProviderName = providerName;
    101         mReadOnly = !allowWrite;
    102 
    103         createAndRegisterObserverProxy(observer);
    104     }
    105 
    106     public void binderDied() {
    107         mCursor.close();
    108         if (mWindow != null) {
    109             mWindow.close();
    110         }
    111     }
    112 
    113     public CursorWindow getWindow(int startPos) {
    114         mCursor.moveToPosition(startPos);
    115 
    116         if (mWindow != null) {
    117             if (startPos < mWindow.getStartPosition() ||
    118                     startPos >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
    119                 mCursor.fillWindow(startPos, mWindow);
    120             }
    121             return mWindow;
    122         } else {
    123             return ((AbstractWindowedCursor)mCursor).getWindow();
    124         }
    125     }
    126 
    127     public void onMove(int position) {
    128         mCursor.onMove(mCursor.getPosition(), position);
    129     }
    130 
    131     public int count() {
    132         return mCursor.getCount();
    133     }
    134 
    135     public String[] getColumnNames() {
    136         return mCursor.getColumnNames();
    137     }
    138 
    139     public void deactivate() {
    140         maybeUnregisterObserverProxy();
    141         mCursor.deactivate();
    142     }
    143 
    144     public void close() {
    145         maybeUnregisterObserverProxy();
    146         mCursor.close();
    147     }
    148 
    149     public int requery(IContentObserver observer, CursorWindow window) {
    150         if (mWindow == null) {
    151             ((AbstractWindowedCursor)mCursor).setWindow(window);
    152         }
    153         try {
    154             if (!mCursor.requery()) {
    155                 return -1;
    156             }
    157         } catch (IllegalStateException e) {
    158             IllegalStateException leakProgram = new IllegalStateException(
    159                     mProviderName + " Requery misuse db, mCursor isClosed:" +
    160                     mCursor.isClosed(), e);
    161             throw leakProgram;
    162         }
    163 
    164         if (mWindow != null) {
    165             mCursor.fillWindow(0, window);
    166             mWindow = window;
    167         }
    168         maybeUnregisterObserverProxy();
    169         createAndRegisterObserverProxy(observer);
    170         return mCursor.getCount();
    171     }
    172 
    173     public boolean getWantsAllOnMoveCalls() {
    174         return mCursor.getWantsAllOnMoveCalls();
    175     }
    176 
    177     /**
    178      * Create a ContentObserver from the observer and register it as an observer on the
    179      * underlying cursor.
    180      * @param observer the IContentObserver that wants to monitor the cursor
    181      * @throws IllegalStateException if an observer is already registered
    182      */
    183     private void createAndRegisterObserverProxy(IContentObserver observer) {
    184         if (mObserver != null) {
    185             throw new IllegalStateException("an observer is already registered");
    186         }
    187         mObserver = new ContentObserverProxy(observer, this);
    188         mCursor.registerContentObserver(mObserver);
    189     }
    190 
    191     /** Unregister the observer if it is already registered. */
    192     private void maybeUnregisterObserverProxy() {
    193         if (mObserver != null) {
    194             mCursor.unregisterContentObserver(mObserver);
    195             mObserver.unlinkToDeath(this);
    196             mObserver = null;
    197         }
    198     }
    199 
    200     public boolean updateRows(Map<? extends Long, ? extends Map<String, Object>> values) {
    201         if (mReadOnly) {
    202             Log.w("ContentProvider", "Permission Denial: modifying "
    203                     + mProviderName
    204                     + " from pid=" + Binder.getCallingPid()
    205                     + ", uid=" + Binder.getCallingUid());
    206             return false;
    207         }
    208         return mCursor.commitUpdates(values);
    209     }
    210 
    211     public boolean deleteRow(int position) {
    212         if (mReadOnly) {
    213             Log.w("ContentProvider", "Permission Denial: modifying "
    214                     + mProviderName
    215                     + " from pid=" + Binder.getCallingPid()
    216                     + ", uid=" + Binder.getCallingUid());
    217             return false;
    218         }
    219         if (mCursor.moveToPosition(position) == false) {
    220             return false;
    221         }
    222         return mCursor.deleteRow();
    223     }
    224 
    225     public Bundle getExtras() {
    226         return mCursor.getExtras();
    227     }
    228 
    229     public Bundle respond(Bundle extras) {
    230         return mCursor.respond(extras);
    231     }
    232 }
    233