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      * @throws IllegalArgumentException if {@code windowSizeBytes} is less than 0
135      * @throws AssertionError if created window pointer is 0
136      * <p><strong>Note:</strong> Memory is dynamically allocated as data rows are added to the
137      * window. Depending on the amount of data stored, the actual amount of memory allocated can be
138      * lower than specified size, but cannot exceed it.
139      */
CursorWindow(String name, @BytesLong long windowSizeBytes)140     public CursorWindow(String name, @BytesLong long windowSizeBytes) {
141         if (windowSizeBytes < 0) {
142             throw new IllegalArgumentException("Window size cannot be less than 0");
143         }
144         mStartPos = 0;
145         mName = name != null && name.length() != 0 ? name : "<unnamed>";
146         mWindowPtr = nativeCreate(mName, (int) windowSizeBytes);
147         if (mWindowPtr == 0) {
148             throw new AssertionError(); // Not possible, the native code won't return it.
149         }
150         mCloseGuard.open("CursorWindow.close");
151     }
152 
153     /**
154      * Creates a new empty cursor window.
155      * <p>
156      * The cursor initially has no rows or columns.  Call {@link #setNumColumns(int)} to
157      * set the number of columns before adding any rows to the cursor.
158      * </p>
159      *
160      * @param localWindow True if this window will be used in this process only,
161      * false if it might be sent to another processes.  This argument is ignored.
162      *
163      * @deprecated There is no longer a distinction between local and remote
164      * cursor windows.  Use the {@link #CursorWindow(String)} constructor instead.
165      */
166     @Deprecated
CursorWindow(boolean localWindow)167     public CursorWindow(boolean localWindow) {
168         this((String)null);
169     }
170 
CursorWindow(Parcel source)171     private CursorWindow(Parcel source) {
172         mStartPos = source.readInt();
173         mWindowPtr = nativeCreateFromParcel(source);
174         if (mWindowPtr == 0) {
175             throw new AssertionError(); // Not possible, the native code won't return it.
176         }
177         mName = nativeGetName(mWindowPtr);
178         mCloseGuard.open("CursorWindow.close");
179     }
180 
181     @Override
finalize()182     protected void finalize() throws Throwable {
183         try {
184             if (mCloseGuard != null) {
185                 mCloseGuard.warnIfOpen();
186             }
187             dispose();
188         } finally {
189             super.finalize();
190         }
191     }
192 
dispose()193     private void dispose() {
194         if (mCloseGuard != null) {
195             mCloseGuard.close();
196         }
197         if (mWindowPtr != 0) {
198             nativeDispose(mWindowPtr);
199             mWindowPtr = 0;
200         }
201     }
202 
203     /**
204      * Gets the name of this cursor window, never null.
205      * @hide
206      */
getName()207     public String getName() {
208         return mName;
209     }
210 
211     /**
212      * Clears out the existing contents of the window, making it safe to reuse
213      * for new data.
214      * <p>
215      * The start position ({@link #getStartPosition()}), number of rows ({@link #getNumRows()}),
216      * and number of columns in the cursor are all reset to zero.
217      * </p>
218      */
clear()219     public void clear() {
220         acquireReference();
221         try {
222             mStartPos = 0;
223             nativeClear(mWindowPtr);
224         } finally {
225             releaseReference();
226         }
227     }
228 
229     /**
230      * Gets the start position of this cursor window.
231      * <p>
232      * The start position is the zero-based index of the first row that this window contains
233      * relative to the entire result set of the {@link Cursor}.
234      * </p>
235      *
236      * @return The zero-based start position.
237      */
getStartPosition()238     public @IntRange(from = 0) int getStartPosition() {
239         return mStartPos;
240     }
241 
242     /**
243      * Sets the start position of this cursor window.
244      * <p>
245      * The start position is the zero-based index of the first row that this window contains
246      * relative to the entire result set of the {@link Cursor}.
247      * </p>
248      *
249      * @param pos The new zero-based start position.
250      */
setStartPosition(@ntRangefrom = 0) int pos)251     public void setStartPosition(@IntRange(from = 0) int pos) {
252         mStartPos = pos;
253     }
254 
255     /**
256      * Gets the number of rows in this window.
257      *
258      * @return The number of rows in this cursor window.
259      */
getNumRows()260     public @IntRange(from = 0) int getNumRows() {
261         acquireReference();
262         try {
263             return nativeGetNumRows(mWindowPtr);
264         } finally {
265             releaseReference();
266         }
267     }
268 
269     /**
270      * Sets the number of columns in this window.
271      * <p>
272      * This method must be called before any rows are added to the window, otherwise
273      * it will fail to set the number of columns if it differs from the current number
274      * of columns.
275      * </p>
276      *
277      * @param columnNum The new number of columns.
278      * @return True if successful.
279      */
setNumColumns(@ntRangefrom = 0) int columnNum)280     public boolean setNumColumns(@IntRange(from = 0) int columnNum) {
281         acquireReference();
282         try {
283             return nativeSetNumColumns(mWindowPtr, columnNum);
284         } finally {
285             releaseReference();
286         }
287     }
288 
289     /**
290      * Allocates a new row at the end of this cursor window.
291      *
292      * @return True if successful, false if the cursor window is out of memory.
293      */
allocRow()294     public boolean allocRow(){
295         acquireReference();
296         try {
297             return nativeAllocRow(mWindowPtr);
298         } finally {
299             releaseReference();
300         }
301     }
302 
303     /**
304      * Frees the last row in this cursor window.
305      */
freeLastRow()306     public void freeLastRow(){
307         acquireReference();
308         try {
309             nativeFreeLastRow(mWindowPtr);
310         } finally {
311             releaseReference();
312         }
313     }
314 
315     /**
316      * Returns true if the field at the specified row and column index
317      * has type {@link Cursor#FIELD_TYPE_NULL}.
318      *
319      * @param row The zero-based row index.
320      * @param column The zero-based column index.
321      * @return True if the field has type {@link Cursor#FIELD_TYPE_NULL}.
322      * @deprecated Use {@link #getType(int, int)} instead.
323      */
324     @Deprecated
isNull(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)325     public boolean isNull(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
326         return getType(row, column) == Cursor.FIELD_TYPE_NULL;
327     }
328 
329     /**
330      * Returns true if the field at the specified row and column index
331      * has type {@link Cursor#FIELD_TYPE_BLOB} or {@link Cursor#FIELD_TYPE_NULL}.
332      *
333      * @param row The zero-based row index.
334      * @param column The zero-based column index.
335      * @return True if the field has type {@link Cursor#FIELD_TYPE_BLOB} or
336      * {@link Cursor#FIELD_TYPE_NULL}.
337      * @deprecated Use {@link #getType(int, int)} instead.
338      */
339     @Deprecated
isBlob(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)340     public boolean isBlob(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
341         int type = getType(row, column);
342         return type == Cursor.FIELD_TYPE_BLOB || type == Cursor.FIELD_TYPE_NULL;
343     }
344 
345     /**
346      * Returns true if the field at the specified row and column index
347      * has type {@link Cursor#FIELD_TYPE_INTEGER}.
348      *
349      * @param row The zero-based row index.
350      * @param column The zero-based column index.
351      * @return True if the field has type {@link Cursor#FIELD_TYPE_INTEGER}.
352      * @deprecated Use {@link #getType(int, int)} instead.
353      */
354     @Deprecated
isLong(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)355     public boolean isLong(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
356         return getType(row, column) == Cursor.FIELD_TYPE_INTEGER;
357     }
358 
359     /**
360      * Returns true if the field at the specified row and column index
361      * has type {@link Cursor#FIELD_TYPE_FLOAT}.
362      *
363      * @param row The zero-based row index.
364      * @param column The zero-based column index.
365      * @return True if the field has type {@link Cursor#FIELD_TYPE_FLOAT}.
366      * @deprecated Use {@link #getType(int, int)} instead.
367      */
368     @Deprecated
isFloat(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)369     public boolean isFloat(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
370         return getType(row, column) == Cursor.FIELD_TYPE_FLOAT;
371     }
372 
373     /**
374      * Returns true if the field at the specified row and column index
375      * has type {@link Cursor#FIELD_TYPE_STRING} or {@link Cursor#FIELD_TYPE_NULL}.
376      *
377      * @param row The zero-based row index.
378      * @param column The zero-based column index.
379      * @return True if the field has type {@link Cursor#FIELD_TYPE_STRING}
380      * or {@link Cursor#FIELD_TYPE_NULL}.
381      * @deprecated Use {@link #getType(int, int)} instead.
382      */
383     @Deprecated
isString(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)384     public boolean isString(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
385         int type = getType(row, column);
386         return type == Cursor.FIELD_TYPE_STRING || type == Cursor.FIELD_TYPE_NULL;
387     }
388 
389     /**
390      * Returns the type of the field at the specified row and column index.
391      *
392      * @param row The zero-based row index.
393      * @param column The zero-based column index.
394      * @return The field type.
395      */
getType(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)396     public @Cursor.FieldType int getType(@IntRange(from = 0) int row,
397             @IntRange(from = 0) int column) {
398         acquireReference();
399         try {
400             return nativeGetType(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 byte array.
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_BLOB}, then the result
414      * is the blob value.</li>
415      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
416      * is the array of bytes that make up the internal representation of the
417      * string value.</li>
418      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER} or
419      * {@link Cursor#FIELD_TYPE_FLOAT}, then a {@link SQLiteException} is thrown.</li>
420      * </ul>
421      * </p>
422      *
423      * @param row The zero-based row index.
424      * @param column The zero-based column index.
425      * @return The value of the field as a byte array.
426      */
getBlob(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)427     public byte[] getBlob(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
428         acquireReference();
429         try {
430             return nativeGetBlob(mWindowPtr, row - mStartPos, column);
431         } finally {
432             releaseReference();
433         }
434     }
435 
436     /**
437      * Gets the value of the field at the specified row and column index as a string.
438      * <p>
439      * The result is determined as follows:
440      * <ul>
441      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
442      * is <code>null</code>.</li>
443      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
444      * is the string value.</li>
445      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result
446      * is a string representation of the integer in decimal, obtained by formatting the
447      * value with the <code>printf</code> family of functions using
448      * format specifier <code>%lld</code>.</li>
449      * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result
450      * is a string representation of the floating-point value in decimal, obtained by
451      * formatting the value with the <code>printf</code> family of functions using
452      * format specifier <code>%g</code>.</li>
453      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
454      * {@link SQLiteException} is thrown.</li>
455      * </ul>
456      * </p>
457      *
458      * @param row The zero-based row index.
459      * @param column The zero-based column index.
460      * @return The value of the field as a string.
461      */
getString(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)462     public String getString(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
463         acquireReference();
464         try {
465             return nativeGetString(mWindowPtr, row - mStartPos, column);
466         } finally {
467             releaseReference();
468         }
469     }
470 
471     /**
472      * Copies the text of the field at the specified row and column index into
473      * a {@link CharArrayBuffer}.
474      * <p>
475      * The buffer is populated as follows:
476      * <ul>
477      * <li>If the buffer is too small for the value to be copied, then it is
478      * automatically resized.</li>
479      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the buffer
480      * is set to an empty string.</li>
481      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the buffer
482      * is set to the contents of the string.</li>
483      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the buffer
484      * is set to a string representation of the integer in decimal, obtained by formatting the
485      * value with the <code>printf</code> family of functions using
486      * format specifier <code>%lld</code>.</li>
487      * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the buffer is
488      * set to a string representation of the floating-point value in decimal, obtained by
489      * formatting the value with the <code>printf</code> family of functions using
490      * format specifier <code>%g</code>.</li>
491      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
492      * {@link SQLiteException} is thrown.</li>
493      * </ul>
494      * </p>
495      *
496      * @param row The zero-based row index.
497      * @param column The zero-based column index.
498      * @param buffer The {@link CharArrayBuffer} to hold the string.  It is automatically
499      * resized if the requested string is larger than the buffer's current capacity.
500       */
copyStringToBuffer(@ntRangefrom = 0) int row, @IntRange(from = 0) int column, CharArrayBuffer buffer)501     public void copyStringToBuffer(@IntRange(from = 0) int row, @IntRange(from = 0) int column,
502             CharArrayBuffer buffer) {
503         if (buffer == null) {
504             throw new IllegalArgumentException("CharArrayBuffer should not be null");
505         }
506         acquireReference();
507         try {
508             nativeCopyStringToBuffer(mWindowPtr, row - mStartPos, column, buffer);
509         } finally {
510             releaseReference();
511         }
512     }
513 
514     /**
515      * Gets the value of the field at the specified row and column index as a <code>long</code>.
516      * <p>
517      * The result is determined as follows:
518      * <ul>
519      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
520      * is <code>0L</code>.</li>
521      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
522      * is the value obtained by parsing the string value with <code>strtoll</code>.
523      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result
524      * is the <code>long</code> value.</li>
525      * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result
526      * is the floating-point value converted to a <code>long</code>.</li>
527      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
528      * {@link SQLiteException} is thrown.</li>
529      * </ul>
530      * </p>
531      *
532      * @param row The zero-based row index.
533      * @param column The zero-based column index.
534      * @return The value of the field as a <code>long</code>.
535      */
getLong(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)536     public long getLong(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
537         acquireReference();
538         try {
539             return nativeGetLong(mWindowPtr, row - mStartPos, column);
540         } finally {
541             releaseReference();
542         }
543     }
544 
545     /**
546      * Gets the value of the field at the specified row and column index as a
547      * <code>double</code>.
548      * <p>
549      * The result is determined as follows:
550      * <ul>
551      * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
552      * is <code>0.0</code>.</li>
553      * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
554      * is the value obtained by parsing the string value with <code>strtod</code>.
555      * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result
556      * is the integer value converted to a <code>double</code>.</li>
557      * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result
558      * is the <code>double</code> value.</li>
559      * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
560      * {@link SQLiteException} is thrown.</li>
561      * </ul>
562      * </p>
563      *
564      * @param row The zero-based row index.
565      * @param column The zero-based column index.
566      * @return The value of the field as a <code>double</code>.
567      */
getDouble(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)568     public double getDouble(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
569         acquireReference();
570         try {
571             return nativeGetDouble(mWindowPtr, row - mStartPos, column);
572         } finally {
573             releaseReference();
574         }
575     }
576 
577     /**
578      * Gets the value of the field at the specified row and column index as a
579      * <code>short</code>.
580      * <p>
581      * The result is determined by invoking {@link #getLong} and converting the
582      * result to <code>short</code>.
583      * </p>
584      *
585      * @param row The zero-based row index.
586      * @param column The zero-based column index.
587      * @return The value of the field as a <code>short</code>.
588      */
getShort(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)589     public short getShort(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
590         return (short) getLong(row, column);
591     }
592 
593     /**
594      * Gets the value of the field at the specified row and column index as an
595      * <code>int</code>.
596      * <p>
597      * The result is determined by invoking {@link #getLong} and converting the
598      * result to <code>int</code>.
599      * </p>
600      *
601      * @param row The zero-based row index.
602      * @param column The zero-based column index.
603      * @return The value of the field as an <code>int</code>.
604      */
getInt(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)605     public int getInt(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
606         return (int) getLong(row, column);
607     }
608 
609     /**
610      * Gets the value of the field at the specified row and column index as a
611      * <code>float</code>.
612      * <p>
613      * The result is determined by invoking {@link #getDouble} and converting the
614      * result to <code>float</code>.
615      * </p>
616      *
617      * @param row The zero-based row index.
618      * @param column The zero-based column index.
619      * @return The value of the field as an <code>float</code>.
620      */
getFloat(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)621     public float getFloat(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
622         return (float) getDouble(row, column);
623     }
624 
625     /**
626      * Copies a byte array into the field at the specified row and column index.
627      *
628      * @param value The value to store.
629      * @param row The zero-based row index.
630      * @param column The zero-based column index.
631      * @return True if successful.
632      */
putBlob(byte[] value, @IntRange(from = 0) int row, @IntRange(from = 0) int column)633     public boolean putBlob(byte[] value,
634             @IntRange(from = 0) int row, @IntRange(from = 0) int column) {
635         acquireReference();
636         try {
637             return nativePutBlob(mWindowPtr, value, row - mStartPos, column);
638         } finally {
639             releaseReference();
640         }
641     }
642 
643     /**
644      * Copies a string into the field at the specified row and column index.
645      *
646      * @param value The value to store.
647      * @param row The zero-based row index.
648      * @param column The zero-based column index.
649      * @return True if successful.
650      */
putString(String value, @IntRange(from = 0) int row, @IntRange(from = 0) int column)651     public boolean putString(String value,
652             @IntRange(from = 0) int row, @IntRange(from = 0) int column) {
653         acquireReference();
654         try {
655             return nativePutString(mWindowPtr, value, row - mStartPos, column);
656         } finally {
657             releaseReference();
658         }
659     }
660 
661     /**
662      * Puts a long integer into the field at the specified row and column index.
663      *
664      * @param value The value to store.
665      * @param row The zero-based row index.
666      * @param column The zero-based column index.
667      * @return True if successful.
668      */
putLong(long value, @IntRange(from = 0) int row, @IntRange(from = 0) int column)669     public boolean putLong(long value,
670             @IntRange(from = 0) int row, @IntRange(from = 0) 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, @IntRange(from = 0) int row, @IntRange(from = 0) int column)688     public boolean putDouble(double value,
689             @IntRange(from = 0) int row, @IntRange(from = 0) int column) {
690         acquireReference();
691         try {
692             return nativePutDouble(mWindowPtr, value, row - mStartPos, column);
693         } finally {
694             releaseReference();
695         }
696     }
697 
698     /**
699      * Puts a null value into the field at the specified row and column index.
700      *
701      * @param row The zero-based row index.
702      * @param column The zero-based column index.
703      * @return True if successful.
704      */
putNull(@ntRangefrom = 0) int row, @IntRange(from = 0) int column)705     public boolean putNull(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
706         acquireReference();
707         try {
708             return nativePutNull(mWindowPtr, row - mStartPos, column);
709         } finally {
710             releaseReference();
711         }
712     }
713 
714     public static final @android.annotation.NonNull Parcelable.Creator<CursorWindow> CREATOR
715             = new Parcelable.Creator<CursorWindow>() {
716         public CursorWindow createFromParcel(Parcel source) {
717             return new CursorWindow(source);
718         }
719 
720         public CursorWindow[] newArray(int size) {
721             return new CursorWindow[size];
722         }
723     };
724 
newFromParcel(Parcel p)725     public static CursorWindow newFromParcel(Parcel p) {
726         return CREATOR.createFromParcel(p);
727     }
728 
describeContents()729     public int describeContents() {
730         return 0;
731     }
732 
writeToParcel(Parcel dest, int flags)733     public void writeToParcel(Parcel dest, int flags) {
734         acquireReference();
735         try {
736             dest.writeInt(mStartPos);
737             nativeWriteToParcel(mWindowPtr, dest);
738         } finally {
739             releaseReference();
740         }
741 
742         if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
743             releaseReference();
744         }
745     }
746 
747     @Override
onAllReferencesReleased()748     protected void onAllReferencesReleased() {
749         dispose();
750     }
751 
getCursorWindowSize()752     private static int getCursorWindowSize() {
753         if (sCursorWindowSize < 0) {
754             // The cursor window size. resource xml file specifies the value in kB.
755             // convert it to bytes here by multiplying with 1024.
756             sCursorWindowSize = Resources.getSystem().getInteger(
757                     com.android.internal.R.integer.config_cursorWindowSize) * 1024;
758         }
759         return sCursorWindowSize;
760     }
761 
762     @Override
toString()763     public String toString() {
764         return getName() + " {" + Long.toHexString(mWindowPtr) + "}";
765     }
766 }
767