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