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