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