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