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