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