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