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