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