CursorWindow.java revision 80e7b80fa607e13d31d7dab68ddfd4d9f0372e38
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. 275 * @param column The zero-based column index. 276 * @return True if the field has type {@link Cursor#FIELD_TYPE_NULL}. 277 * @deprecated Use {@link #getType(int, int)} instead. 278 */ 279 @Deprecated 280 public boolean isNull(int row, int column) { 281 return getType(row, column) == Cursor.FIELD_TYPE_NULL; 282 } 283 284 /** 285 * Returns true if the field at the specified row and column index 286 * has type {@link Cursor#FIELD_TYPE_BLOB} or {@link Cursor#FIELD_TYPE_NULL}. 287 * 288 * @param row The zero-based row index. 289 * @param column The zero-based column index. 290 * @return True if the field has type {@link Cursor#FIELD_TYPE_BLOB} or 291 * {@link Cursor#FIELD_TYPE_NULL}. 292 * @deprecated Use {@link #getType(int, int)} instead. 293 */ 294 @Deprecated 295 public boolean isBlob(int row, int column) { 296 int type = getType(row, column); 297 return type == Cursor.FIELD_TYPE_BLOB || type == Cursor.FIELD_TYPE_NULL; 298 } 299 300 /** 301 * Returns true if the field at the specified row and column index 302 * has type {@link Cursor#FIELD_TYPE_INTEGER}. 303 * 304 * @param row The zero-based row index. 305 * @param column The zero-based column index. 306 * @return True if the field has type {@link Cursor#FIELD_TYPE_INTEGER}. 307 * @deprecated Use {@link #getType(int, int)} instead. 308 */ 309 @Deprecated 310 public boolean isLong(int row, int column) { 311 return getType(row, column) == Cursor.FIELD_TYPE_INTEGER; 312 } 313 314 /** 315 * Returns true if the field at the specified row and column index 316 * has type {@link Cursor#FIELD_TYPE_FLOAT}. 317 * 318 * @param row The zero-based row index. 319 * @param column The zero-based column index. 320 * @return True if the field has type {@link Cursor#FIELD_TYPE_FLOAT}. 321 * @deprecated Use {@link #getType(int, int)} instead. 322 */ 323 @Deprecated 324 public boolean isFloat(int row, int column) { 325 return getType(row, column) == Cursor.FIELD_TYPE_FLOAT; 326 } 327 328 /** 329 * Returns true if the field at the specified row and column index 330 * has type {@link Cursor#FIELD_TYPE_STRING} or {@link Cursor#FIELD_TYPE_NULL}. 331 * 332 * @param row The zero-based row index. 333 * @param column The zero-based column index. 334 * @return True if the field has type {@link Cursor#FIELD_TYPE_STRING} 335 * or {@link Cursor#FIELD_TYPE_NULL}. 336 * @deprecated Use {@link #getType(int, int)} instead. 337 */ 338 @Deprecated 339 public boolean isString(int row, int column) { 340 int type = getType(row, column); 341 return type == Cursor.FIELD_TYPE_STRING || type == Cursor.FIELD_TYPE_NULL; 342 } 343 344 /** 345 * Returns the type of the field at the specified row and column index. 346 * <p> 347 * The returned field types are: 348 * <ul> 349 * <li>{@link Cursor#FIELD_TYPE_NULL}</li> 350 * <li>{@link Cursor#FIELD_TYPE_INTEGER}</li> 351 * <li>{@link Cursor#FIELD_TYPE_FLOAT}</li> 352 * <li>{@link Cursor#FIELD_TYPE_STRING}</li> 353 * <li>{@link Cursor#FIELD_TYPE_BLOB}</li> 354 * </ul> 355 * </p> 356 * 357 * @param row The zero-based row index. 358 * @param column The zero-based column index. 359 * @return The field type. 360 */ 361 public int getType(int row, int column) { 362 acquireReference(); 363 try { 364 return nativeGetType(mWindowPtr, row - mStartPos, column); 365 } finally { 366 releaseReference(); 367 } 368 } 369 370 /** 371 * Gets the value of the field at the specified row and column index as a byte array. 372 * <p> 373 * The result is determined as follows: 374 * <ul> 375 * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result 376 * is <code>null</code>.</li> 377 * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then the result 378 * is the blob value.</li> 379 * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result 380 * is the array of bytes that make up the internal representation of the 381 * string value.</li> 382 * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER} or 383 * {@link Cursor#FIELD_TYPE_FLOAT}, then a {@link SQLiteException} is thrown.</li> 384 * </ul> 385 * </p> 386 * 387 * @param row The zero-based row index. 388 * @param column The zero-based column index. 389 * @return The value of the field as a byte array. 390 */ 391 public byte[] getBlob(int row, int column) { 392 acquireReference(); 393 try { 394 return nativeGetBlob(mWindowPtr, row - mStartPos, column); 395 } finally { 396 releaseReference(); 397 } 398 } 399 400 /** 401 * Gets the value of the field at the specified row and column index as a string. 402 * <p> 403 * The result is determined as follows: 404 * <ul> 405 * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result 406 * is <code>null</code>.</li> 407 * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result 408 * is the string value.</li> 409 * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result 410 * is a string representation of the integer in decimal, obtained by formatting the 411 * value with the <code>printf</code> family of functions using 412 * format specifier <code>%lld</code>.</li> 413 * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result 414 * is a string representation of the floating-point value in decimal, obtained by 415 * formatting the value with the <code>printf</code> family of functions using 416 * format specifier <code>%g</code>.</li> 417 * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a 418 * {@link SQLiteException} is thrown.</li> 419 * </ul> 420 * </p> 421 * 422 * @param row The zero-based row index. 423 * @param column The zero-based column index. 424 * @return The value of the field as a string. 425 */ 426 public String getString(int row, int column) { 427 acquireReference(); 428 try { 429 return nativeGetString(mWindowPtr, row - mStartPos, column); 430 } finally { 431 releaseReference(); 432 } 433 } 434 435 /** 436 * Copies the text of the field at the specified row and column index into 437 * a {@link CharArrayBuffer}. 438 * <p> 439 * The buffer is populated as follows: 440 * <ul> 441 * <li>If the buffer is too small for the value to be copied, then it is 442 * automatically resized.</li> 443 * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the buffer 444 * is set to an empty string.</li> 445 * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the buffer 446 * is set to the contents of the string.</li> 447 * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the buffer 448 * is set to a string representation of the integer in decimal, obtained by formatting the 449 * value with the <code>printf</code> family of functions using 450 * format specifier <code>%lld</code>.</li> 451 * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the buffer is 452 * set to a string representation of the floating-point value in decimal, obtained by 453 * formatting the value with the <code>printf</code> family of functions using 454 * format specifier <code>%g</code>.</li> 455 * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a 456 * {@link SQLiteException} is thrown.</li> 457 * </ul> 458 * </p> 459 * 460 * @param row The zero-based row index. 461 * @param column The zero-based column index. 462 * @param buffer The {@link CharArrayBuffer} to hold the string. It is automatically 463 * resized if the requested string is larger than the buffer's current capacity. 464 */ 465 public void copyStringToBuffer(int row, int column, CharArrayBuffer buffer) { 466 if (buffer == null) { 467 throw new IllegalArgumentException("CharArrayBuffer should not be null"); 468 } 469 acquireReference(); 470 try { 471 nativeCopyStringToBuffer(mWindowPtr, row, column, buffer); 472 } finally { 473 releaseReference(); 474 } 475 } 476 477 /** 478 * Gets the value of the field at the specified row and column index as a <code>long</code>. 479 * <p> 480 * The result is determined as follows: 481 * <ul> 482 * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result 483 * is <code>0L</code>.</li> 484 * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result 485 * is the value obtained by parsing the string value with <code>strtoll</code>. 486 * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result 487 * is the <code>long</code> value.</li> 488 * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result 489 * is the floating-point value converted to a <code>long</code>.</li> 490 * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a 491 * {@link SQLiteException} is thrown.</li> 492 * </ul> 493 * </p> 494 * 495 * @param row The zero-based row index. 496 * @param column The zero-based column index. 497 * @return The value of the field as a <code>long</code>. 498 */ 499 public long getLong(int row, int column) { 500 acquireReference(); 501 try { 502 return nativeGetLong(mWindowPtr, row - mStartPos, column); 503 } finally { 504 releaseReference(); 505 } 506 } 507 508 /** 509 * Gets the value of the field at the specified row and column index as a 510 * <code>double</code>. 511 * <p> 512 * The result is determined as follows: 513 * <ul> 514 * <li>If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result 515 * is <code>0.0</code>.</li> 516 * <li>If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result 517 * is the value obtained by parsing the string value with <code>strtod</code>. 518 * <li>If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result 519 * is the integer value converted to a <code>double</code>.</li> 520 * <li>If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result 521 * is the <code>double</code> value.</li> 522 * <li>If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a 523 * {@link SQLiteException} is thrown.</li> 524 * </ul> 525 * </p> 526 * 527 * @param row The zero-based row index. 528 * @param column The zero-based column index. 529 * @return The value of the field as a <code>double</code>. 530 */ 531 public double getDouble(int row, int column) { 532 acquireReference(); 533 try { 534 return nativeGetDouble(mWindowPtr, row - mStartPos, column); 535 } finally { 536 releaseReference(); 537 } 538 } 539 540 /** 541 * Gets the value of the field at the specified row and column index as a 542 * <code>short</code>. 543 * <p> 544 * The result is determined by invoking {@link #getLong} and converting the 545 * result to <code>short</code>. 546 * </p> 547 * 548 * @param row The zero-based row index. 549 * @param column The zero-based column index. 550 * @return The value of the field as a <code>short</code>. 551 */ 552 public short getShort(int row, int column) { 553 return (short) getLong(row, column); 554 } 555 556 /** 557 * Gets the value of the field at the specified row and column index as an 558 * <code>int</code>. 559 * <p> 560 * The result is determined by invoking {@link #getLong} and converting the 561 * result to <code>int</code>. 562 * </p> 563 * 564 * @param row The zero-based row index. 565 * @param column The zero-based column index. 566 * @return The value of the field as an <code>int</code>. 567 */ 568 public int getInt(int row, int column) { 569 return (int) getLong(row, column); 570 } 571 572 /** 573 * Gets the value of the field at the specified row and column index as a 574 * <code>float</code>. 575 * <p> 576 * The result is determined by invoking {@link #getDouble} and converting the 577 * result to <code>float</code>. 578 * </p> 579 * 580 * @param row The zero-based row index. 581 * @param column The zero-based column index. 582 * @return The value of the field as an <code>float</code>. 583 */ 584 public float getFloat(int row, int column) { 585 return (float) getDouble(row, column); 586 } 587 588 /** 589 * Copies a byte array into the field at the specified row and column index. 590 * 591 * @param value The value to store. 592 * @param row The zero-based row index. 593 * @param column The zero-based column index. 594 * @return True if successful. 595 */ 596 public boolean putBlob(byte[] value, int row, int column) { 597 acquireReference(); 598 try { 599 return nativePutBlob(mWindowPtr, value, row - mStartPos, column); 600 } finally { 601 releaseReference(); 602 } 603 } 604 605 /** 606 * Copies a string into the field at the specified row and column index. 607 * 608 * @param value The value to store. 609 * @param row The zero-based row index. 610 * @param column The zero-based column index. 611 * @return True if successful. 612 */ 613 public boolean putString(String value, int row, int column) { 614 acquireReference(); 615 try { 616 return nativePutString(mWindowPtr, value, row - mStartPos, column); 617 } finally { 618 releaseReference(); 619 } 620 } 621 622 /** 623 * Puts a long integer into the field at the specified row and column index. 624 * 625 * @param value The value to store. 626 * @param row The zero-based row index. 627 * @param column The zero-based column index. 628 * @return True if successful. 629 */ 630 public boolean putLong(long value, int row, int column) { 631 acquireReference(); 632 try { 633 return nativePutLong(mWindowPtr, value, row - mStartPos, column); 634 } finally { 635 releaseReference(); 636 } 637 } 638 639 /** 640 * Puts a double-precision floating point value into the field at the 641 * specified row and column index. 642 * 643 * @param value The value to store. 644 * @param row The zero-based row index. 645 * @param column The zero-based column index. 646 * @return True if successful. 647 */ 648 public boolean putDouble(double value, int row, int column) { 649 acquireReference(); 650 try { 651 return nativePutDouble(mWindowPtr, value, row - mStartPos, column); 652 } finally { 653 releaseReference(); 654 } 655 } 656 657 /** 658 * Puts a null value into the field at the specified row and column index. 659 * 660 * @param row The zero-based row index. 661 * @param column The zero-based column index. 662 * @return True if successful. 663 */ 664 public boolean putNull(int row, int column) { 665 acquireReference(); 666 try { 667 return nativePutNull(mWindowPtr, row - mStartPos, column); 668 } finally { 669 releaseReference(); 670 } 671 } 672 673 public static final Parcelable.Creator<CursorWindow> CREATOR 674 = new Parcelable.Creator<CursorWindow>() { 675 public CursorWindow createFromParcel(Parcel source) { 676 return new CursorWindow(source); 677 } 678 679 public CursorWindow[] newArray(int size) { 680 return new CursorWindow[size]; 681 } 682 }; 683 684 public static CursorWindow newFromParcel(Parcel p) { 685 return CREATOR.createFromParcel(p); 686 } 687 688 public int describeContents() { 689 return 0; 690 } 691 692 public void writeToParcel(Parcel dest, int flags) { 693 dest.writeInt(mStartPos); 694 nativeWriteToParcel(mWindowPtr, dest); 695 696 if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) { 697 releaseReference(); 698 } 699 } 700 701 @Override 702 protected void onAllReferencesReleased() { 703 dispose(); 704 } 705 706 private static final SparseIntArray sWindowToPidMap = new SparseIntArray(); 707 708 private void recordNewWindow(int pid, int window) { 709 synchronized (sWindowToPidMap) { 710 sWindowToPidMap.put(window, pid); 711 if (Log.isLoggable(STATS_TAG, Log.VERBOSE)) { 712 Log.i(STATS_TAG, "Created a new Cursor. " + printStats()); 713 } 714 } 715 } 716 717 private void recordClosingOfWindow(int window) { 718 synchronized (sWindowToPidMap) { 719 if (sWindowToPidMap.size() == 0) { 720 // this means we are not in the ContentProvider. 721 return; 722 } 723 sWindowToPidMap.delete(window); 724 } 725 } 726 727 private String printStats() { 728 StringBuilder buff = new StringBuilder(); 729 int myPid = Process.myPid(); 730 int total = 0; 731 SparseIntArray pidCounts = new SparseIntArray(); 732 synchronized (sWindowToPidMap) { 733 int size = sWindowToPidMap.size(); 734 if (size == 0) { 735 // this means we are not in the ContentProvider. 736 return ""; 737 } 738 for (int indx = 0; indx < size; indx++) { 739 int pid = sWindowToPidMap.valueAt(indx); 740 int value = pidCounts.get(pid); 741 pidCounts.put(pid, ++value); 742 } 743 } 744 int numPids = pidCounts.size(); 745 for (int i = 0; i < numPids;i++) { 746 buff.append(" (# cursors opened by "); 747 int pid = pidCounts.keyAt(i); 748 if (pid == myPid) { 749 buff.append("this proc="); 750 } else { 751 buff.append("pid " + pid + "="); 752 } 753 int num = pidCounts.get(pid); 754 buff.append(num + ")"); 755 total += num; 756 } 757 // limit the returned string size to 1000 758 String s = (buff.length() > 980) ? buff.substring(0, 980) : buff.toString(); 759 return "# Open Cursors=" + total + s; 760 } 761} 762