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