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