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