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