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