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