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 != null && name.length() != 0 ? name : "<unnamed>";
    102         mWindowPtr = nativeCreate(mName, 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, never null.
    165      * @hide
    166      */
    167     public String getName() {
    168         return mName;
    169     }
    170 
    171     /**
    172      * Clears out the existing contents of the window, making it safe to reuse
    173      * for new data.
    174      * <p>
    175      * The start position ({@link #getStartPosition()}), number of rows ({@link #getNumRows()}),
    176      * and number of columns in the cursor are all reset to zero.
    177      * </p>
    178      */
    179     public void clear() {
    180         acquireReference();
    181         try {
    182             mStartPos = 0;
    183             nativeClear(mWindowPtr);
    184         } finally {
    185             releaseReference();
    186         }
    187     }
    188 
    189     /**
    190      * Gets the start position of this cursor window.
    191      * <p>
    192      * The start position is the zero-based index of the first row that this window contains
    193      * relative to the entire result set of the {@link Cursor}.
    194      * </p>
    195      *
    196      * @return The zero-based start position.
    197      */
    198     public int getStartPosition() {
    199         return mStartPos;
    200     }
    201 
    202     /**
    203      * Sets the start position of this cursor window.
    204      * <p>
    205      * The start position is the zero-based index of the first row that this window contains
    206      * relative to the entire result set of the {@link Cursor}.
    207      * </p>
    208      *
    209      * @param pos The new zero-based start position.
    210      */
    211     public void setStartPosition(int pos) {
    212         mStartPos = pos;
    213     }
    214 
    215     /**
    216      * Gets the number of rows in this window.
    217      *
    218      * @return The number of rows in this cursor window.
    219      */
    220     public int getNumRows() {
    221         acquireReference();
    222         try {
    223             return nativeGetNumRows(mWindowPtr);
    224         } finally {
    225             releaseReference();
    226         }
    227     }
    228 
    229     /**
    230      * Sets the number of columns in this window.
    231      * <p>
    232      * This method must be called before any rows are added to the window, otherwise
    233      * it will fail to set the number of columns if it differs from the current number
    234      * of columns.
    235      * </p>
    236      *
    237      * @param columnNum The new number of columns.
    238      * @return True if successful.
    239      */
    240     public boolean setNumColumns(int columnNum) {
    241         acquireReference();
    242         try {
    243             return nativeSetNumColumns(mWindowPtr, columnNum);
    244         } finally {
    245             releaseReference();
    246         }
    247     }
    248 
    249     /**
    250      * Allocates a new row at the end of this cursor window.
    251      *
    252      * @return True if successful, false if the cursor window is out of memory.
    253      */
    254     public boolean allocRow(){
    255         acquireReference();
    256         try {
    257             return nativeAllocRow(mWindowPtr);
    258         } finally {
    259             releaseReference();
    260         }
    261     }
    262 
    263     /**
    264      * Frees the last row in this cursor window.
    265      */
    266     public void freeLastRow(){
    267         acquireReference();
    268         try {
    269             nativeFreeLastRow(mWindowPtr);
    270         } finally {
    271             releaseReference();
    272         }
    273     }
    274 
    275     /**
    276      * Returns true if the field at the specified row and column index
    277      * has type {@link Cursor#FIELD_TYPE_NULL}.
    278      *
    279      * @param row The zero-based row index.
    280      * @param column The zero-based column index.
    281      * @return True if the field has type {@link Cursor#FIELD_TYPE_NULL}.
    282      * @deprecated Use {@link #getType(int, int)} instead.
    283      */
    284     @Deprecated
    285     public boolean isNull(int row, int column) {
    286         return getType(row, column) == Cursor.FIELD_TYPE_NULL;
    287     }
    288 
    289     /**
    290      * Returns true if the field at the specified row and column index
    291      * has type {@link Cursor#FIELD_TYPE_BLOB} or {@link Cursor#FIELD_TYPE_NULL}.
    292      *
    293      * @param row The zero-based row index.
    294      * @param column The zero-based column index.
    295      * @return True if the field has type {@link Cursor#FIELD_TYPE_BLOB} or
    296      * {@link Cursor#FIELD_TYPE_NULL}.
    297      * @deprecated Use {@link #getType(int, int)} instead.
    298      */
    299     @Deprecated
    300     public boolean isBlob(int row, int column) {
    301         int type = getType(row, column);
    302         return type == Cursor.FIELD_TYPE_BLOB || type == Cursor.FIELD_TYPE_NULL;
    303     }
    304 
    305     /**
    306      * Returns true if the field at the specified row and column index
    307      * has type {@link Cursor#FIELD_TYPE_INTEGER}.
    308      *
    309      * @param row The zero-based row index.
    310      * @param column The zero-based column index.
    311      * @return True if the field has type {@link Cursor#FIELD_TYPE_INTEGER}.
    312      * @deprecated Use {@link #getType(int, int)} instead.
    313      */
    314     @Deprecated
    315     public boolean isLong(int row, int column) {
    316         return getType(row, column) == Cursor.FIELD_TYPE_INTEGER;
    317     }
    318 
    319     /**
    320      * Returns true if the field at the specified row and column index
    321      * has type {@link Cursor#FIELD_TYPE_FLOAT}.
    322      *
    323      * @param row The zero-based row index.
    324      * @param column The zero-based column index.
    325      * @return True if the field has type {@link Cursor#FIELD_TYPE_FLOAT}.
    326      * @deprecated Use {@link #getType(int, int)} instead.
    327      */
    328     @Deprecated
    329     public boolean isFloat(int row, int column) {
    330         return getType(row, column) == Cursor.FIELD_TYPE_FLOAT;
    331     }
    332 
    333     /**
    334      * Returns true if the field at the specified row and column index
    335      * has type {@link Cursor#FIELD_TYPE_STRING} or {@link Cursor#FIELD_TYPE_NULL}.
    336      *
    337      * @param row The zero-based row index.
    338      * @param column The zero-based column index.
    339      * @return True if the field has type {@link Cursor#FIELD_TYPE_STRING}
    340      * or {@link Cursor#FIELD_TYPE_NULL}.
    341      * @deprecated Use {@link #getType(int, int)} instead.
    342      */
    343     @Deprecated
    344     public boolean isString(int row, int column) {
    345         int type = getType(row, column);
    346         return type == Cursor.FIELD_TYPE_STRING || type == Cursor.FIELD_TYPE_NULL;
    347     }
    348 
    349     /**
    350      * Returns the type of the field at the specified row and column index.
    351      * <p>
    352      * The returned field types are:
    353      * <ul>
    354      * <li>{@link Cursor#FIELD_TYPE_NULL}</li>
    355      * <li>{@link Cursor#FIELD_TYPE_INTEGER}</li>
    356      * <li>{@link Cursor#FIELD_TYPE_FLOAT}</li>
    357      * <li>{@link Cursor#FIELD_TYPE_STRING}</li>
    358      * <li>{@link Cursor#FIELD_TYPE_BLOB}</li>
    359      * </ul>
    360      * </p>
    361      *
    362      * @param row The zero-based row index.
    363      * @param column The zero-based column index.
    364      * @return The field type.
    365      */
    366     public int getType(int row, int column) {
    367         acquireReference();
    368         try {
    369             return nativeGetType(mWindowPtr, row - mStartPos, column);
    370         } finally {
    371             releaseReference();
    372         }
    373     }
    374 
    375     /**
    376      * Gets the value of the field at the specified row and column index as a byte array.
    377      * <p>
    378      * The result is determined as follows:
    379      * <ul>
    380      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
    381      * is <code>null</code>.</li>
    382      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then the result
    383      * is the blob value.</li>
    384      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
    385      * is the array of bytes that make up the internal representation of the
    386      * string value.</li>
    387      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER} or
    388      * {@link Cursor#FIELD_TYPE_FLOAT}, then a {@link SQLiteException} is thrown.</li>
    389      * </ul>
    390      * </p>
    391      *
    392      * @param row The zero-based row index.
    393      * @param column The zero-based column index.
    394      * @return The value of the field as a byte array.
    395      */
    396     public byte[] getBlob(int row, int column) {
    397         acquireReference();
    398         try {
    399             return nativeGetBlob(mWindowPtr, row - mStartPos, column);
    400         } finally {
    401             releaseReference();
    402         }
    403     }
    404 
    405     /**
    406      * Gets the value of the field at the specified row and column index as a string.
    407      * <p>
    408      * The result is determined as follows:
    409      * <ul>
    410      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
    411      * is <code>null</code>.</li>
    412      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
    413      * is the string value.</li>
    414      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result
    415      * is a string representation of the integer in decimal, obtained by formatting the
    416      * value with the <code>printf</code> family of functions using
    417      * format specifier <code>%lld</code>.</li>
    418      * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result
    419      * is a string representation of the floating-point value in decimal, obtained by
    420      * formatting the value with the <code>printf</code> family of functions using
    421      * format specifier <code>%g</code>.</li>
    422      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
    423      * {@link SQLiteException} is thrown.</li>
    424      * </ul>
    425      * </p>
    426      *
    427      * @param row The zero-based row index.
    428      * @param column The zero-based column index.
    429      * @return The value of the field as a string.
    430      */
    431     public String getString(int row, int column) {
    432         acquireReference();
    433         try {
    434             return nativeGetString(mWindowPtr, row - mStartPos, column);
    435         } finally {
    436             releaseReference();
    437         }
    438     }
    439 
    440     /**
    441      * Copies the text of the field at the specified row and column index into
    442      * a {@link CharArrayBuffer}.
    443      * <p>
    444      * The buffer is populated as follows:
    445      * <ul>
    446      * <li>If the buffer is too small for the value to be copied, then it is
    447      * automatically resized.</li>
    448      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the buffer
    449      * is set to an empty string.</li>
    450      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the buffer
    451      * is set to the contents of the string.</li>
    452      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the buffer
    453      * is set to a string representation of the integer in decimal, obtained by formatting the
    454      * value with the <code>printf</code> family of functions using
    455      * format specifier <code>%lld</code>.</li>
    456      * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the buffer is
    457      * set to a string representation of the floating-point value in decimal, obtained by
    458      * formatting the value with the <code>printf</code> family of functions using
    459      * format specifier <code>%g</code>.</li>
    460      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
    461      * {@link SQLiteException} is thrown.</li>
    462      * </ul>
    463      * </p>
    464      *
    465      * @param row The zero-based row index.
    466      * @param column The zero-based column index.
    467      * @param buffer The {@link CharArrayBuffer} to hold the string.  It is automatically
    468      * resized if the requested string is larger than the buffer's current capacity.
    469       */
    470     public void copyStringToBuffer(int row, int column, CharArrayBuffer buffer) {
    471         if (buffer == null) {
    472             throw new IllegalArgumentException("CharArrayBuffer should not be null");
    473         }
    474         acquireReference();
    475         try {
    476             nativeCopyStringToBuffer(mWindowPtr, row - mStartPos, column, buffer);
    477         } finally {
    478             releaseReference();
    479         }
    480     }
    481 
    482     /**
    483      * Gets the value of the field at the specified row and column index as a <code>long</code>.
    484      * <p>
    485      * The result is determined as follows:
    486      * <ul>
    487      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
    488      * is <code>0L</code>.</li>
    489      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
    490      * is the value obtained by parsing the string value with <code>strtoll</code>.
    491      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result
    492      * is the <code>long</code> value.</li>
    493      * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result
    494      * is the floating-point value converted to a <code>long</code>.</li>
    495      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
    496      * {@link SQLiteException} is thrown.</li>
    497      * </ul>
    498      * </p>
    499      *
    500      * @param row The zero-based row index.
    501      * @param column The zero-based column index.
    502      * @return The value of the field as a <code>long</code>.
    503      */
    504     public long getLong(int row, int column) {
    505         acquireReference();
    506         try {
    507             return nativeGetLong(mWindowPtr, row - mStartPos, column);
    508         } finally {
    509             releaseReference();
    510         }
    511     }
    512 
    513     /**
    514      * Gets the value of the field at the specified row and column index as a
    515      * <code>double</code>.
    516      * <p>
    517      * The result is determined as follows:
    518      * <ul>
    519      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
    520      * is <code>0.0</code>.</li>
    521      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
    522      * is the value obtained by parsing the string value with <code>strtod</code>.
    523      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result
    524      * is the integer value converted to a <code>double</code>.</li>
    525      * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result
    526      * is the <code>double</code> value.</li>
    527      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
    528      * {@link SQLiteException} is thrown.</li>
    529      * </ul>
    530      * </p>
    531      *
    532      * @param row The zero-based row index.
    533      * @param column The zero-based column index.
    534      * @return The value of the field as a <code>double</code>.
    535      */
    536     public double getDouble(int row, int column) {
    537         acquireReference();
    538         try {
    539             return nativeGetDouble(mWindowPtr, row - mStartPos, column);
    540         } finally {
    541             releaseReference();
    542         }
    543     }
    544 
    545     /**
    546      * Gets the value of the field at the specified row and column index as a
    547      * <code>short</code>.
    548      * <p>
    549      * The result is determined by invoking {@link #getLong} and converting the
    550      * result to <code>short</code>.
    551      * </p>
    552      *
    553      * @param row The zero-based row index.
    554      * @param column The zero-based column index.
    555      * @return The value of the field as a <code>short</code>.
    556      */
    557     public short getShort(int row, int column) {
    558         return (short) getLong(row, column);
    559     }
    560 
    561     /**
    562      * Gets the value of the field at the specified row and column index as an
    563      * <code>int</code>.
    564      * <p>
    565      * The result is determined by invoking {@link #getLong} and converting the
    566      * result to <code>int</code>.
    567      * </p>
    568      *
    569      * @param row The zero-based row index.
    570      * @param column The zero-based column index.
    571      * @return The value of the field as an <code>int</code>.
    572      */
    573     public int getInt(int row, int column) {
    574         return (int) getLong(row, column);
    575     }
    576 
    577     /**
    578      * Gets the value of the field at the specified row and column index as a
    579      * <code>float</code>.
    580      * <p>
    581      * The result is determined by invoking {@link #getDouble} and converting the
    582      * result to <code>float</code>.
    583      * </p>
    584      *
    585      * @param row The zero-based row index.
    586      * @param column The zero-based column index.
    587      * @return The value of the field as an <code>float</code>.
    588      */
    589     public float getFloat(int row, int column) {
    590         return (float) getDouble(row, column);
    591     }
    592 
    593     /**
    594      * Copies a byte array into the field at the specified row and column index.
    595      *
    596      * @param value The value to store.
    597      * @param row The zero-based row index.
    598      * @param column The zero-based column index.
    599      * @return True if successful.
    600      */
    601     public boolean putBlob(byte[] value, int row, int column) {
    602         acquireReference();
    603         try {
    604             return nativePutBlob(mWindowPtr, value, row - mStartPos, column);
    605         } finally {
    606             releaseReference();
    607         }
    608     }
    609 
    610     /**
    611      * Copies a string into the field at the specified row and column index.
    612      *
    613      * @param value The value to store.
    614      * @param row The zero-based row index.
    615      * @param column The zero-based column index.
    616      * @return True if successful.
    617      */
    618     public boolean putString(String value, int row, int column) {
    619         acquireReference();
    620         try {
    621             return nativePutString(mWindowPtr, value, row - mStartPos, column);
    622         } finally {
    623             releaseReference();
    624         }
    625     }
    626 
    627     /**
    628      * Puts a long integer into the field at the specified row and column index.
    629      *
    630      * @param value The value to store.
    631      * @param row The zero-based row index.
    632      * @param column The zero-based column index.
    633      * @return True if successful.
    634      */
    635     public boolean putLong(long value, int row, int column) {
    636         acquireReference();
    637         try {
    638             return nativePutLong(mWindowPtr, value, row - mStartPos, column);
    639         } finally {
    640             releaseReference();
    641         }
    642     }
    643 
    644     /**
    645      * Puts a double-precision floating point value into the field at the
    646      * specified row and column index.
    647      *
    648      * @param value The value to store.
    649      * @param row The zero-based row index.
    650      * @param column The zero-based column index.
    651      * @return True if successful.
    652      */
    653     public boolean putDouble(double value, int row, int column) {
    654         acquireReference();
    655         try {
    656             return nativePutDouble(mWindowPtr, value, row - mStartPos, column);
    657         } finally {
    658             releaseReference();
    659         }
    660     }
    661 
    662     /**
    663      * Puts a null value into the field at the specified row and column index.
    664      *
    665      * @param row The zero-based row index.
    666      * @param column The zero-based column index.
    667      * @return True if successful.
    668      */
    669     public boolean putNull(int row, int column) {
    670         acquireReference();
    671         try {
    672             return nativePutNull(mWindowPtr, row - mStartPos, column);
    673         } finally {
    674             releaseReference();
    675         }
    676     }
    677 
    678     public static final Parcelable.Creator<CursorWindow> CREATOR
    679             = new Parcelable.Creator<CursorWindow>() {
    680         public CursorWindow createFromParcel(Parcel source) {
    681             return new CursorWindow(source);
    682         }
    683 
    684         public CursorWindow[] newArray(int size) {
    685             return new CursorWindow[size];
    686         }
    687     };
    688 
    689     public static CursorWindow newFromParcel(Parcel p) {
    690         return CREATOR.createFromParcel(p);
    691     }
    692 
    693     public int describeContents() {
    694         return 0;
    695     }
    696 
    697     public void writeToParcel(Parcel dest, int flags) {
    698         acquireReference();
    699         try {
    700             dest.writeInt(mStartPos);
    701             nativeWriteToParcel(mWindowPtr, dest);
    702         } finally {
    703             releaseReference();
    704         }
    705 
    706         if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
    707             releaseReference();
    708         }
    709     }
    710 
    711     @Override
    712     protected void onAllReferencesReleased() {
    713         dispose();
    714     }
    715 
    716     private static final SparseIntArray sWindowToPidMap = new SparseIntArray();
    717 
    718     private void recordNewWindow(int pid, int window) {
    719         synchronized (sWindowToPidMap) {
    720             sWindowToPidMap.put(window, pid);
    721             if (Log.isLoggable(STATS_TAG, Log.VERBOSE)) {
    722                 Log.i(STATS_TAG, "Created a new Cursor. " + printStats());
    723             }
    724         }
    725     }
    726 
    727     private void recordClosingOfWindow(int window) {
    728         synchronized (sWindowToPidMap) {
    729             if (sWindowToPidMap.size() == 0) {
    730                 // this means we are not in the ContentProvider.
    731                 return;
    732             }
    733             sWindowToPidMap.delete(window);
    734         }
    735     }
    736 
    737     private String printStats() {
    738         StringBuilder buff = new StringBuilder();
    739         int myPid = Process.myPid();
    740         int total = 0;
    741         SparseIntArray pidCounts = new SparseIntArray();
    742         synchronized (sWindowToPidMap) {
    743             int size = sWindowToPidMap.size();
    744             if (size == 0) {
    745                 // this means we are not in the ContentProvider.
    746                 return "";
    747             }
    748             for (int indx = 0; indx < size; indx++) {
    749                 int pid = sWindowToPidMap.valueAt(indx);
    750                 int value = pidCounts.get(pid);
    751                 pidCounts.put(pid, ++value);
    752             }
    753         }
    754         int numPids = pidCounts.size();
    755         for (int i = 0; i < numPids;i++) {
    756             buff.append(" (# cursors opened by ");
    757             int pid = pidCounts.keyAt(i);
    758             if (pid == myPid) {
    759                 buff.append("this proc=");
    760             } else {
    761                 buff.append("pid " + pid + "=");
    762             }
    763             int num = pidCounts.get(pid);
    764             buff.append(num + ")");
    765             total += num;
    766         }
    767         // limit the returned string size to 1000
    768         String s = (buff.length() > 980) ? buff.substring(0, 980) : buff.toString();
    769         return "# Open Cursors=" + total + s;
    770     }
    771 
    772     @Override
    773     public String toString() {
    774         return getName() + " {" + Integer.toHexString(mWindowPtr) + "}";
    775     }
    776 }
    777