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 dalvik.system.CloseGuard;
     20 
     21 import android.content.res.Resources;
     22 import android.database.sqlite.SQLiteClosable;
     23 import android.database.sqlite.SQLiteException;
     24 import android.os.Binder;
     25 import android.os.Parcel;
     26 import android.os.Parcelable;
     27 import android.os.Process;
     28 import android.util.Log;
     29 import android.util.SparseIntArray;
     30 
     31 /**
     32  * A buffer containing multiple cursor rows.
     33  * <p>
     34  * A {@link CursorWindow} is read-write when initially created and used locally.
     35  * When sent to a remote process (by writing it to a {@link Parcel}), the remote process
     36  * receives a read-only view of the cursor window.  Typically the cursor window
     37  * will be allocated by the producer, filled with data, and then sent to the
     38  * consumer for reading.
     39  * </p>
     40  */
     41 public class CursorWindow extends SQLiteClosable implements Parcelable {
     42     private static final String STATS_TAG = "CursorWindowStats";
     43 
     44     /** The cursor window size. resource xml file specifies the value in kB.
     45      * convert it to bytes here by multiplying with 1024.
     46      */
     47     private static final int sCursorWindowSize =
     48         Resources.getSystem().getInteger(
     49                 com.android.internal.R.integer.config_cursorWindowSize) * 1024;
     50 
     51     /**
     52      * The native CursorWindow object pointer.  (FOR INTERNAL USE ONLY)
     53      * @hide
     54      */
     55     public int mWindowPtr;
     56 
     57     private int mStartPos;
     58     private final String mName;
     59 
     60     private final CloseGuard mCloseGuard = CloseGuard.get();
     61 
     62     private static native int nativeCreate(String name, int cursorWindowSize);
     63     private static native int nativeCreateFromParcel(Parcel parcel);
     64     private static native void nativeDispose(int windowPtr);
     65     private static native void nativeWriteToParcel(int windowPtr, Parcel parcel);
     66 
     67     private static native void nativeClear(int windowPtr);
     68 
     69     private static native int nativeGetNumRows(int windowPtr);
     70     private static native boolean nativeSetNumColumns(int windowPtr, int columnNum);
     71     private static native boolean nativeAllocRow(int windowPtr);
     72     private static native void nativeFreeLastRow(int windowPtr);
     73 
     74     private static native int nativeGetType(int windowPtr, int row, int column);
     75     private static native byte[] nativeGetBlob(int windowPtr, int row, int column);
     76     private static native String nativeGetString(int windowPtr, int row, int column);
     77     private static native long nativeGetLong(int windowPtr, int row, int column);
     78     private static native double nativeGetDouble(int windowPtr, int row, int column);
     79     private static native void nativeCopyStringToBuffer(int windowPtr, int row, int column,
     80             CharArrayBuffer buffer);
     81 
     82     private static native boolean nativePutBlob(int windowPtr, byte[] value, int row, int column);
     83     private static native boolean nativePutString(int windowPtr, String value, int row, int column);
     84     private static native boolean nativePutLong(int windowPtr, long value, int row, int column);
     85     private static native boolean nativePutDouble(int windowPtr, double value, int row, int column);
     86     private static native boolean nativePutNull(int windowPtr, int row, int column);
     87 
     88     private static native String nativeGetName(int windowPtr);
     89 
     90     /**
     91      * Creates a new empty cursor window and gives it a name.
     92      * <p>
     93      * The cursor initially has no rows or columns.  Call {@link #setNumColumns(int)} to
     94      * set the number of columns before adding any rows to the cursor.
     95      * </p>
     96      *
     97      * @param name The name of the cursor window, or null if none.
     98      */
     99     public CursorWindow(String name) {
    100         mStartPos = 0;
    101         mName = name;
    102         mWindowPtr = nativeCreate(name, sCursorWindowSize);
    103         if (mWindowPtr == 0) {
    104             throw new CursorWindowAllocationException("Cursor window allocation of " +
    105                     (sCursorWindowSize / 1024) + " kb failed. " + printStats());
    106         }
    107         mCloseGuard.open("close");
    108         recordNewWindow(Binder.getCallingPid(), mWindowPtr);
    109     }
    110 
    111     /**
    112      * Creates a new empty cursor window.
    113      * <p>
    114      * The cursor initially has no rows or columns.  Call {@link #setNumColumns(int)} to
    115      * set the number of columns before adding any rows to the cursor.
    116      * </p>
    117      *
    118      * @param localWindow True if this window will be used in this process only,
    119      * false if it might be sent to another processes.  This argument is ignored.
    120      *
    121      * @deprecated There is no longer a distinction between local and remote
    122      * cursor windows.  Use the {@link #CursorWindow(String)} constructor instead.
    123      */
    124     @Deprecated
    125     public CursorWindow(boolean localWindow) {
    126         this((String)null);
    127     }
    128 
    129     private CursorWindow(Parcel source) {
    130         mStartPos = source.readInt();
    131         mWindowPtr = nativeCreateFromParcel(source);
    132         if (mWindowPtr == 0) {
    133             throw new CursorWindowAllocationException("Cursor window could not be "
    134                     + "created from binder.");
    135         }
    136         mName = nativeGetName(mWindowPtr);
    137         mCloseGuard.open("close");
    138     }
    139 
    140     @Override
    141     protected void finalize() throws Throwable {
    142         try {
    143             if (mCloseGuard != null) {
    144                 mCloseGuard.warnIfOpen();
    145             }
    146             dispose();
    147         } finally {
    148             super.finalize();
    149         }
    150     }
    151 
    152     private void dispose() {
    153         if (mCloseGuard != null) {
    154             mCloseGuard.close();
    155         }
    156         if (mWindowPtr != 0) {
    157             recordClosingOfWindow(mWindowPtr);
    158             nativeDispose(mWindowPtr);
    159             mWindowPtr = 0;
    160         }
    161     }
    162 
    163     /**
    164      * Gets the name of this cursor window.
    165      * @hide
    166      */
    167     public String getName() {
    168         return mName;
    169     }
    170 
    171     /**
    172      * Closes the cursor window and frees its underlying resources when all other
    173      * remaining references have been released.
    174      */
    175     public void close() {
    176         releaseReference();
    177     }
    178 
    179     /**
    180      * Clears out the existing contents of the window, making it safe to reuse
    181      * for new data.
    182      * <p>
    183      * The start position ({@link #getStartPosition()}), number of rows ({@link #getNumRows()}),
    184      * and number of columns in the cursor are all reset to zero.
    185      * </p>
    186      */
    187     public void clear() {
    188         acquireReference();
    189         try {
    190             mStartPos = 0;
    191             nativeClear(mWindowPtr);
    192         } finally {
    193             releaseReference();
    194         }
    195     }
    196 
    197     /**
    198      * Gets the start position of this cursor window.
    199      * <p>
    200      * The start position is the zero-based index of the first row that this window contains
    201      * relative to the entire result set of the {@link Cursor}.
    202      * </p>
    203      *
    204      * @return The zero-based start position.
    205      */
    206     public int getStartPosition() {
    207         return mStartPos;
    208     }
    209 
    210     /**
    211      * Sets the start position of this cursor window.
    212      * <p>
    213      * The start position is the zero-based index of the first row that this window contains
    214      * relative to the entire result set of the {@link Cursor}.
    215      * </p>
    216      *
    217      * @param pos The new zero-based start position.
    218      */
    219     public void setStartPosition(int pos) {
    220         mStartPos = pos;
    221     }
    222 
    223     /**
    224      * Gets the number of rows in this window.
    225      *
    226      * @return The number of rows in this cursor window.
    227      */
    228     public int getNumRows() {
    229         acquireReference();
    230         try {
    231             return nativeGetNumRows(mWindowPtr);
    232         } finally {
    233             releaseReference();
    234         }
    235     }
    236 
    237     /**
    238      * Sets the number of columns in this window.
    239      * <p>
    240      * This method must be called before any rows are added to the window, otherwise
    241      * it will fail to set the number of columns if it differs from the current number
    242      * of columns.
    243      * </p>
    244      *
    245      * @param columnNum The new number of columns.
    246      * @return True if successful.
    247      */
    248     public boolean setNumColumns(int columnNum) {
    249         acquireReference();
    250         try {
    251             return nativeSetNumColumns(mWindowPtr, columnNum);
    252         } finally {
    253             releaseReference();
    254         }
    255     }
    256 
    257     /**
    258      * Allocates a new row at the end of this cursor window.
    259      *
    260      * @return True if successful, false if the cursor window is out of memory.
    261      */
    262     public boolean allocRow(){
    263         acquireReference();
    264         try {
    265             return nativeAllocRow(mWindowPtr);
    266         } finally {
    267             releaseReference();
    268         }
    269     }
    270 
    271     /**
    272      * Frees the last row in this cursor window.
    273      */
    274     public void freeLastRow(){
    275         acquireReference();
    276         try {
    277             nativeFreeLastRow(mWindowPtr);
    278         } finally {
    279             releaseReference();
    280         }
    281     }
    282 
    283     /**
    284      * Returns true if the field at the specified row and column index
    285      * has type {@link Cursor#FIELD_TYPE_NULL}.
    286      *
    287      * @param row The zero-based row index.
    288      * @param column The zero-based column index.
    289      * @return True if the field has type {@link Cursor#FIELD_TYPE_NULL}.
    290      * @deprecated Use {@link #getType(int, int)} instead.
    291      */
    292     @Deprecated
    293     public boolean isNull(int row, int column) {
    294         return getType(row, column) == Cursor.FIELD_TYPE_NULL;
    295     }
    296 
    297     /**
    298      * Returns true if the field at the specified row and column index
    299      * has type {@link Cursor#FIELD_TYPE_BLOB} or {@link Cursor#FIELD_TYPE_NULL}.
    300      *
    301      * @param row The zero-based row index.
    302      * @param column The zero-based column index.
    303      * @return True if the field has type {@link Cursor#FIELD_TYPE_BLOB} or
    304      * {@link Cursor#FIELD_TYPE_NULL}.
    305      * @deprecated Use {@link #getType(int, int)} instead.
    306      */
    307     @Deprecated
    308     public boolean isBlob(int row, int column) {
    309         int type = getType(row, column);
    310         return type == Cursor.FIELD_TYPE_BLOB || type == Cursor.FIELD_TYPE_NULL;
    311     }
    312 
    313     /**
    314      * Returns true if the field at the specified row and column index
    315      * has type {@link Cursor#FIELD_TYPE_INTEGER}.
    316      *
    317      * @param row The zero-based row index.
    318      * @param column The zero-based column index.
    319      * @return True if the field has type {@link Cursor#FIELD_TYPE_INTEGER}.
    320      * @deprecated Use {@link #getType(int, int)} instead.
    321      */
    322     @Deprecated
    323     public boolean isLong(int row, int column) {
    324         return getType(row, column) == Cursor.FIELD_TYPE_INTEGER;
    325     }
    326 
    327     /**
    328      * Returns true if the field at the specified row and column index
    329      * has type {@link Cursor#FIELD_TYPE_FLOAT}.
    330      *
    331      * @param row The zero-based row index.
    332      * @param column The zero-based column index.
    333      * @return True if the field has type {@link Cursor#FIELD_TYPE_FLOAT}.
    334      * @deprecated Use {@link #getType(int, int)} instead.
    335      */
    336     @Deprecated
    337     public boolean isFloat(int row, int column) {
    338         return getType(row, column) == Cursor.FIELD_TYPE_FLOAT;
    339     }
    340 
    341     /**
    342      * Returns true if the field at the specified row and column index
    343      * has type {@link Cursor#FIELD_TYPE_STRING} or {@link Cursor#FIELD_TYPE_NULL}.
    344      *
    345      * @param row The zero-based row index.
    346      * @param column The zero-based column index.
    347      * @return True if the field has type {@link Cursor#FIELD_TYPE_STRING}
    348      * or {@link Cursor#FIELD_TYPE_NULL}.
    349      * @deprecated Use {@link #getType(int, int)} instead.
    350      */
    351     @Deprecated
    352     public boolean isString(int row, int column) {
    353         int type = getType(row, column);
    354         return type == Cursor.FIELD_TYPE_STRING || type == Cursor.FIELD_TYPE_NULL;
    355     }
    356 
    357     /**
    358      * Returns the type of the field at the specified row and column index.
    359      * <p>
    360      * The returned field types are:
    361      * <ul>
    362      * <li>{@link Cursor#FIELD_TYPE_NULL}</li>
    363      * <li>{@link Cursor#FIELD_TYPE_INTEGER}</li>
    364      * <li>{@link Cursor#FIELD_TYPE_FLOAT}</li>
    365      * <li>{@link Cursor#FIELD_TYPE_STRING}</li>
    366      * <li>{@link Cursor#FIELD_TYPE_BLOB}</li>
    367      * </ul>
    368      * </p>
    369      *
    370      * @param row The zero-based row index.
    371      * @param column The zero-based column index.
    372      * @return The field type.
    373      */
    374     public int getType(int row, int column) {
    375         acquireReference();
    376         try {
    377             return nativeGetType(mWindowPtr, row - mStartPos, column);
    378         } finally {
    379             releaseReference();
    380         }
    381     }
    382 
    383     /**
    384      * Gets the value of the field at the specified row and column index as a byte array.
    385      * <p>
    386      * The result is determined as follows:
    387      * <ul>
    388      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
    389      * is <code>null</code>.</li>
    390      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then the result
    391      * is the blob value.</li>
    392      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
    393      * is the array of bytes that make up the internal representation of the
    394      * string value.</li>
    395      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER} or
    396      * {@link Cursor#FIELD_TYPE_FLOAT}, then a {@link SQLiteException} is thrown.</li>
    397      * </ul>
    398      * </p>
    399      *
    400      * @param row The zero-based row index.
    401      * @param column The zero-based column index.
    402      * @return The value of the field as a byte array.
    403      */
    404     public byte[] getBlob(int row, int column) {
    405         acquireReference();
    406         try {
    407             return nativeGetBlob(mWindowPtr, row - mStartPos, column);
    408         } finally {
    409             releaseReference();
    410         }
    411     }
    412 
    413     /**
    414      * Gets the value of the field at the specified row and column index as a string.
    415      * <p>
    416      * The result is determined as follows:
    417      * <ul>
    418      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
    419      * is <code>null</code>.</li>
    420      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
    421      * is the string value.</li>
    422      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result
    423      * is a string representation of the integer in decimal, obtained by formatting the
    424      * value with the <code>printf</code> family of functions using
    425      * format specifier <code>%lld</code>.</li>
    426      * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result
    427      * is a string representation of the floating-point value in decimal, obtained by
    428      * formatting the value with the <code>printf</code> family of functions using
    429      * format specifier <code>%g</code>.</li>
    430      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
    431      * {@link SQLiteException} is thrown.</li>
    432      * </ul>
    433      * </p>
    434      *
    435      * @param row The zero-based row index.
    436      * @param column The zero-based column index.
    437      * @return The value of the field as a string.
    438      */
    439     public String getString(int row, int column) {
    440         acquireReference();
    441         try {
    442             return nativeGetString(mWindowPtr, row - mStartPos, column);
    443         } finally {
    444             releaseReference();
    445         }
    446     }
    447 
    448     /**
    449      * Copies the text of the field at the specified row and column index into
    450      * a {@link CharArrayBuffer}.
    451      * <p>
    452      * The buffer is populated as follows:
    453      * <ul>
    454      * <li>If the buffer is too small for the value to be copied, then it is
    455      * automatically resized.</li>
    456      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the buffer
    457      * is set to an empty string.</li>
    458      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the buffer
    459      * is set to the contents of the string.</li>
    460      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the buffer
    461      * is set to a string representation of the integer in decimal, obtained by formatting the
    462      * value with the <code>printf</code> family of functions using
    463      * format specifier <code>%lld</code>.</li>
    464      * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the buffer is
    465      * set to a string representation of the floating-point value in decimal, obtained by
    466      * formatting the value with the <code>printf</code> family of functions using
    467      * format specifier <code>%g</code>.</li>
    468      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
    469      * {@link SQLiteException} is thrown.</li>
    470      * </ul>
    471      * </p>
    472      *
    473      * @param row The zero-based row index.
    474      * @param column The zero-based column index.
    475      * @param buffer The {@link CharArrayBuffer} to hold the string.  It is automatically
    476      * resized if the requested string is larger than the buffer's current capacity.
    477       */
    478     public void copyStringToBuffer(int row, int column, CharArrayBuffer buffer) {
    479         if (buffer == null) {
    480             throw new IllegalArgumentException("CharArrayBuffer should not be null");
    481         }
    482         acquireReference();
    483         try {
    484             nativeCopyStringToBuffer(mWindowPtr, row - mStartPos, column, buffer);
    485         } finally {
    486             releaseReference();
    487         }
    488     }
    489 
    490     /**
    491      * Gets the value of the field at the specified row and column index as a <code>long</code>.
    492      * <p>
    493      * The result is determined as follows:
    494      * <ul>
    495      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
    496      * is <code>0L</code>.</li>
    497      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
    498      * is the value obtained by parsing the string value with <code>strtoll</code>.
    499      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result
    500      * is the <code>long</code> value.</li>
    501      * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result
    502      * is the floating-point value converted to a <code>long</code>.</li>
    503      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
    504      * {@link SQLiteException} is thrown.</li>
    505      * </ul>
    506      * </p>
    507      *
    508      * @param row The zero-based row index.
    509      * @param column The zero-based column index.
    510      * @return The value of the field as a <code>long</code>.
    511      */
    512     public long getLong(int row, int column) {
    513         acquireReference();
    514         try {
    515             return nativeGetLong(mWindowPtr, row - mStartPos, column);
    516         } finally {
    517             releaseReference();
    518         }
    519     }
    520 
    521     /**
    522      * Gets the value of the field at the specified row and column index as a
    523      * <code>double</code>.
    524      * <p>
    525      * The result is determined as follows:
    526      * <ul>
    527      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
    528      * is <code>0.0</code>.</li>
    529      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
    530      * is the value obtained by parsing the string value with <code>strtod</code>.
    531      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result
    532      * is the integer value converted to a <code>double</code>.</li>
    533      * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result
    534      * is the <code>double</code> value.</li>
    535      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
    536      * {@link SQLiteException} is thrown.</li>
    537      * </ul>
    538      * </p>
    539      *
    540      * @param row The zero-based row index.
    541      * @param column The zero-based column index.
    542      * @return The value of the field as a <code>double</code>.
    543      */
    544     public double getDouble(int row, int column) {
    545         acquireReference();
    546         try {
    547             return nativeGetDouble(mWindowPtr, row - mStartPos, column);
    548         } finally {
    549             releaseReference();
    550         }
    551     }
    552 
    553     /**
    554      * Gets the value of the field at the specified row and column index as a
    555      * <code>short</code>.
    556      * <p>
    557      * The result is determined by invoking {@link #getLong} and converting the
    558      * result to <code>short</code>.
    559      * </p>
    560      *
    561      * @param row The zero-based row index.
    562      * @param column The zero-based column index.
    563      * @return The value of the field as a <code>short</code>.
    564      */
    565     public short getShort(int row, int column) {
    566         return (short) getLong(row, column);
    567     }
    568 
    569     /**
    570      * Gets the value of the field at the specified row and column index as an
    571      * <code>int</code>.
    572      * <p>
    573      * The result is determined by invoking {@link #getLong} and converting the
    574      * result to <code>int</code>.
    575      * </p>
    576      *
    577      * @param row The zero-based row index.
    578      * @param column The zero-based column index.
    579      * @return The value of the field as an <code>int</code>.
    580      */
    581     public int getInt(int row, int column) {
    582         return (int) getLong(row, column);
    583     }
    584 
    585     /**
    586      * Gets the value of the field at the specified row and column index as a
    587      * <code>float</code>.
    588      * <p>
    589      * The result is determined by invoking {@link #getDouble} and converting the
    590      * result to <code>float</code>.
    591      * </p>
    592      *
    593      * @param row The zero-based row index.
    594      * @param column The zero-based column index.
    595      * @return The value of the field as an <code>float</code>.
    596      */
    597     public float getFloat(int row, int column) {
    598         return (float) getDouble(row, column);
    599     }
    600 
    601     /**
    602      * Copies a byte array into the field at the specified row and column index.
    603      *
    604      * @param value The value to store.
    605      * @param row The zero-based row index.
    606      * @param column The zero-based column index.
    607      * @return True if successful.
    608      */
    609     public boolean putBlob(byte[] value, int row, int column) {
    610         acquireReference();
    611         try {
    612             return nativePutBlob(mWindowPtr, value, row - mStartPos, column);
    613         } finally {
    614             releaseReference();
    615         }
    616     }
    617 
    618     /**
    619      * Copies a string into the field at the specified row and column index.
    620      *
    621      * @param value The value to store.
    622      * @param row The zero-based row index.
    623      * @param column The zero-based column index.
    624      * @return True if successful.
    625      */
    626     public boolean putString(String value, int row, int column) {
    627         acquireReference();
    628         try {
    629             return nativePutString(mWindowPtr, value, row - mStartPos, column);
    630         } finally {
    631             releaseReference();
    632         }
    633     }
    634 
    635     /**
    636      * Puts a long integer into the field at the specified row and column index.
    637      *
    638      * @param value The value to store.
    639      * @param row The zero-based row index.
    640      * @param column The zero-based column index.
    641      * @return True if successful.
    642      */
    643     public boolean putLong(long value, int row, int column) {
    644         acquireReference();
    645         try {
    646             return nativePutLong(mWindowPtr, value, row - mStartPos, column);
    647         } finally {
    648             releaseReference();
    649         }
    650     }
    651 
    652     /**
    653      * Puts a double-precision floating point value into the field at the
    654      * specified row and column index.
    655      *
    656      * @param value The value to store.
    657      * @param row The zero-based row index.
    658      * @param column The zero-based column index.
    659      * @return True if successful.
    660      */
    661     public boolean putDouble(double value, int row, int column) {
    662         acquireReference();
    663         try {
    664             return nativePutDouble(mWindowPtr, value, row - mStartPos, column);
    665         } finally {
    666             releaseReference();
    667         }
    668     }
    669 
    670     /**
    671      * Puts a null value into the field at the specified row and column index.
    672      *
    673      * @param row The zero-based row index.
    674      * @param column The zero-based column index.
    675      * @return True if successful.
    676      */
    677     public boolean putNull(int row, int column) {
    678         acquireReference();
    679         try {
    680             return nativePutNull(mWindowPtr, row - mStartPos, column);
    681         } finally {
    682             releaseReference();
    683         }
    684     }
    685 
    686     public static final Parcelable.Creator<CursorWindow> CREATOR
    687             = new Parcelable.Creator<CursorWindow>() {
    688         public CursorWindow createFromParcel(Parcel source) {
    689             return new CursorWindow(source);
    690         }
    691 
    692         public CursorWindow[] newArray(int size) {
    693             return new CursorWindow[size];
    694         }
    695     };
    696 
    697     public static CursorWindow newFromParcel(Parcel p) {
    698         return CREATOR.createFromParcel(p);
    699     }
    700 
    701     public int describeContents() {
    702         return 0;
    703     }
    704 
    705     public void writeToParcel(Parcel dest, int flags) {
    706         dest.writeInt(mStartPos);
    707         nativeWriteToParcel(mWindowPtr, dest);
    708 
    709         if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
    710             releaseReference();
    711         }
    712     }
    713 
    714     @Override
    715     protected void onAllReferencesReleased() {
    716         dispose();
    717     }
    718 
    719     private static final SparseIntArray sWindowToPidMap = new SparseIntArray();
    720 
    721     private void recordNewWindow(int pid, int window) {
    722         synchronized (sWindowToPidMap) {
    723             sWindowToPidMap.put(window, pid);
    724             if (Log.isLoggable(STATS_TAG, Log.VERBOSE)) {
    725                 Log.i(STATS_TAG, "Created a new Cursor. " + printStats());
    726             }
    727         }
    728     }
    729 
    730     private void recordClosingOfWindow(int window) {
    731         synchronized (sWindowToPidMap) {
    732             if (sWindowToPidMap.size() == 0) {
    733                 // this means we are not in the ContentProvider.
    734                 return;
    735             }
    736             sWindowToPidMap.delete(window);
    737         }
    738     }
    739 
    740     private String printStats() {
    741         StringBuilder buff = new StringBuilder();
    742         int myPid = Process.myPid();
    743         int total = 0;
    744         SparseIntArray pidCounts = new SparseIntArray();
    745         synchronized (sWindowToPidMap) {
    746             int size = sWindowToPidMap.size();
    747             if (size == 0) {
    748                 // this means we are not in the ContentProvider.
    749                 return "";
    750             }
    751             for (int indx = 0; indx < size; indx++) {
    752                 int pid = sWindowToPidMap.valueAt(indx);
    753                 int value = pidCounts.get(pid);
    754                 pidCounts.put(pid, ++value);
    755             }
    756         }
    757         int numPids = pidCounts.size();
    758         for (int i = 0; i < numPids;i++) {
    759             buff.append(" (# cursors opened by ");
    760             int pid = pidCounts.keyAt(i);
    761             if (pid == myPid) {
    762                 buff.append("this proc=");
    763             } else {
    764                 buff.append("pid " + pid + "=");
    765             }
    766             int num = pidCounts.get(pid);
    767             buff.append(num + ")");
    768             total += num;
    769         }
    770         // limit the returned string size to 1000
    771         String s = (buff.length() > 980) ? buff.substring(0, 980) : buff.toString();
    772         return "# Open Cursors=" + total + s;
    773     }
    774 
    775     @Override
    776     public String toString() {
    777         return getName() + " {" + Integer.toHexString(mWindowPtr) + "}";
    778     }
    779 }
    780