android_database_CursorWindow.cpp revision 34ad57f0e844cd97f59d4ab22087d60d58650ba4
1/* 2 * Copyright (C) 2007 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#undef LOG_TAG 18#define LOG_TAG "CursorWindow" 19 20#include <jni.h> 21#include <JNIHelp.h> 22#include <android_runtime/AndroidRuntime.h> 23 24#include <utils/Log.h> 25#include <utils/String8.h> 26#include <utils/String16.h> 27 28#include <stdio.h> 29#include <string.h> 30#include <unistd.h> 31 32#include "binder/CursorWindow.h" 33#include "sqlite3_exception.h" 34#include "android_util_Binder.h" 35 36 37namespace android { 38 39static jfieldID gWindowField; 40static jfieldID gBufferField; 41static jfieldID gSizeCopiedField; 42 43#define GET_WINDOW(env, object) ((CursorWindow *)env->GetIntField(object, gWindowField)) 44#define SET_WINDOW(env, object, window) (env->SetIntField(object, gWindowField, (int)window)) 45#define SET_BUFFER(env, object, buf) (env->SetObjectField(object, gBufferField, buf)) 46#define SET_SIZE_COPIED(env, object, size) (env->SetIntField(object, gSizeCopiedField, size)) 47 48CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow) 49{ 50 return GET_WINDOW(env, javaWindow); 51} 52 53static void native_init_empty(JNIEnv * env, jobject object, jint cursorWindowSize, 54 jboolean localOnly) 55{ 56 uint8_t * data; 57 size_t size; 58 CursorWindow * window; 59 60 window = new CursorWindow(cursorWindowSize); 61 if (!window) { 62 jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object"); 63 return; 64 } 65 66 if (!window->initBuffer(localOnly)) { 67 jniThrowException(env, "java/lang/RuntimeException", 68 "Memory couldn't be allocated for 1MB CursorWindow object."); 69 delete window; 70 return; 71 } 72 73LOG_WINDOW("native_init_empty: window = %p", window); 74 SET_WINDOW(env, object, window); 75} 76 77static void native_init_memory(JNIEnv * env, jobject object, jobject memObj) 78{ 79 sp<IMemory> memory = interface_cast<IMemory>(ibinderForJavaObject(env, memObj)); 80 if (memory == NULL) { 81 jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder"); 82 return; 83 } 84 85 CursorWindow * window = new CursorWindow(); 86 if (!window) { 87 jniThrowException(env, "java/lang/RuntimeException", 88 "CursorWindow of size 1MB couldn't be created. No memory?"); 89 return; 90 } 91 if (!window->setMemory(memory)) { 92 jniThrowException(env, "java/lang/RuntimeException", 93 "Memory couldn't be initialized for 1MB CursorWindow object."); 94 delete window; 95 return; 96 } 97 98LOG_WINDOW("native_init_memory: numRows = %d, numColumns = %d, window = %p", window->getNumRows(), window->getNumColumns(), window); 99 SET_WINDOW(env, object, window); 100} 101 102static jobject native_getBinder(JNIEnv * env, jobject object) 103{ 104 CursorWindow * window = GET_WINDOW(env, object); 105 if (window) { 106 sp<IMemory> memory = window->getMemory(); 107 if (memory != NULL) { 108 sp<IBinder> binder = memory->asBinder(); 109 return javaObjectForIBinder(env, binder); 110 } 111 } 112 return NULL; 113} 114 115static void native_clear(JNIEnv * env, jobject object) 116{ 117 CursorWindow * window = GET_WINDOW(env, object); 118LOG_WINDOW("Clearing window %p", window); 119 if (window == NULL) { 120 jniThrowException(env, "java/lang/IllegalStateException", "clear() called after close()"); 121 return; 122 } 123 window->clear(); 124} 125 126static void native_close(JNIEnv * env, jobject object) 127{ 128 CursorWindow * window = GET_WINDOW(env, object); 129 if (window) { 130LOG_WINDOW("Closing window %p", window); 131 delete window; 132 SET_WINDOW(env, object, 0); 133 } 134} 135 136static void throwExceptionWithRowCol(JNIEnv * env, jint row, jint column) 137{ 138 char buf[200]; 139 snprintf(buf, sizeof(buf), "Couldn't read row %d, col %d from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it", 140 row, column); 141 jniThrowException(env, "java/lang/IllegalStateException", buf); 142} 143 144static void throwUnknowTypeException(JNIEnv * env, jint type) 145{ 146 char buf[80]; 147 snprintf(buf, sizeof(buf), "UNKNOWN type %d", type); 148 jniThrowException(env, "java/lang/IllegalStateException", buf); 149} 150 151static jlong getLong_native(JNIEnv * env, jobject object, jint row, jint column) 152{ 153 int32_t err; 154 CursorWindow * window = GET_WINDOW(env, object); 155LOG_WINDOW("Getting long for %d,%d from %p", row, column, window); 156 157 field_slot_t field; 158 err = window->read_field_slot(row, column, &field); 159 if (err != 0) { 160 throwExceptionWithRowCol(env, row, column); 161 return 0; 162 } 163 164 uint8_t type = field.type; 165 if (type == FIELD_TYPE_INTEGER) { 166 int64_t value; 167 if (window->getLong(row, column, &value)) { 168 return value; 169 } 170 return 0; 171 } else if (type == FIELD_TYPE_STRING) { 172 uint32_t size = field.data.buffer.size; 173 if (size > 0) { 174#if WINDOW_STORAGE_UTF8 175 return strtoll((char const *)window->offsetToPtr(field.data.buffer.offset), NULL, 0); 176#else 177 String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2); 178 char const * str = ascii.string(); 179 return strtoll(str, NULL, 0); 180#endif 181 } else { 182 return 0; 183 } 184 } else if (type == FIELD_TYPE_FLOAT) { 185 double value; 186 if (window->getDouble(row, column, &value)) { 187 return value; 188 } 189 return 0; 190 } else if (type == FIELD_TYPE_NULL) { 191 return 0; 192 } else if (type == FIELD_TYPE_BLOB) { 193 throw_sqlite3_exception(env, "Unable to convert BLOB to long"); 194 return 0; 195 } else { 196 throwUnknowTypeException(env, type); 197 return 0; 198 } 199} 200 201static jbyteArray getBlob_native(JNIEnv* env, jobject object, jint row, jint column) 202{ 203 int32_t err; 204 CursorWindow * window = GET_WINDOW(env, object); 205LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window); 206 207 field_slot_t field; 208 err = window->read_field_slot(row, column, &field); 209 if (err != 0) { 210 throwExceptionWithRowCol(env, row, column); 211 return NULL; 212 } 213 214 uint8_t type = field.type; 215 if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) { 216 jbyteArray byteArray = env->NewByteArray(field.data.buffer.size); 217 LOG_ASSERT(byteArray, "Native could not create new byte[]"); 218 env->SetByteArrayRegion(byteArray, 0, field.data.buffer.size, 219 (const jbyte*)window->offsetToPtr(field.data.buffer.offset)); 220 return byteArray; 221 } else if (type == FIELD_TYPE_INTEGER) { 222 throw_sqlite3_exception(env, "INTEGER data in getBlob_native "); 223 } else if (type == FIELD_TYPE_FLOAT) { 224 throw_sqlite3_exception(env, "FLOAT data in getBlob_native "); 225 } else if (type == FIELD_TYPE_NULL) { 226 // do nothing 227 } else { 228 throwUnknowTypeException(env, type); 229 } 230 return NULL; 231} 232 233static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column) 234{ 235 int32_t err; 236 CursorWindow * window = GET_WINDOW(env, object); 237LOG_WINDOW("Getting string for %d,%d from %p", row, column, window); 238 239 field_slot_t field; 240 err = window->read_field_slot(row, column, &field); 241 if (err != 0) { 242 throwExceptionWithRowCol(env, row, column); 243 return NULL; 244 } 245 246 uint8_t type = field.type; 247 if (type == FIELD_TYPE_STRING) { 248 uint32_t size = field.data.buffer.size; 249 if (size > 0) { 250#if WINDOW_STORAGE_UTF8 251 // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string 252 String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); 253 return env->NewString((jchar const *)utf16.string(), utf16.size()); 254#else 255 return env->NewString((jchar const *)window->offsetToPtr(field.data.buffer.offset), size / 2); 256#endif 257 } else { 258 return env->NewStringUTF(""); 259 } 260 } else if (type == FIELD_TYPE_INTEGER) { 261 int64_t value; 262 if (window->getLong(row, column, &value)) { 263 char buf[32]; 264 snprintf(buf, sizeof(buf), "%lld", value); 265 return env->NewStringUTF(buf); 266 } 267 return NULL; 268 } else if (type == FIELD_TYPE_FLOAT) { 269 double value; 270 if (window->getDouble(row, column, &value)) { 271 char buf[32]; 272 snprintf(buf, sizeof(buf), "%g", value); 273 return env->NewStringUTF(buf); 274 } 275 return NULL; 276 } else if (type == FIELD_TYPE_NULL) { 277 return NULL; 278 } else if (type == FIELD_TYPE_BLOB) { 279 throw_sqlite3_exception(env, "Unable to convert BLOB to string"); 280 return NULL; 281 } else { 282 throwUnknowTypeException(env, type); 283 return NULL; 284 } 285} 286 287/** 288 * Use this only to convert characters that are known to be within the 289 * 0-127 range for direct conversion to UTF-16 290 */ 291static jint charToJchar(const char* src, jchar* dst, jint bufferSize) 292{ 293 int32_t len = strlen(src); 294 295 if (bufferSize < len) { 296 len = bufferSize; 297 } 298 299 for (int i = 0; i < len; i++) { 300 *dst++ = (*src++ & 0x7F); 301 } 302 return len; 303} 304 305static jcharArray copyStringToBuffer_native(JNIEnv* env, jobject object, jint row, 306 jint column, jint bufferSize, jobject buf) 307{ 308 int32_t err; 309 CursorWindow * window = GET_WINDOW(env, object); 310LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); 311 312 field_slot_t field; 313 err = window->read_field_slot(row, column, &field); 314 if (err != 0) { 315 jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot"); 316 return NULL; 317 } 318 319 jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField); 320 if (buffer == NULL) { 321 jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null"); 322 return NULL; 323 } 324 jchar* dst = env->GetCharArrayElements(buffer, NULL); 325 uint8_t type = field.type; 326 uint32_t sizeCopied = 0; 327 jcharArray newArray = NULL; 328 if (type == FIELD_TYPE_STRING) { 329 uint32_t size = field.data.buffer.size; 330 if (size > 0) { 331#if WINDOW_STORAGE_UTF8 332 // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string 333 String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); 334 int32_t strSize = utf16.size(); 335 if (strSize > bufferSize || dst == NULL) { 336 newArray = env->NewCharArray(strSize); 337 env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string()); 338 } else { 339 memcpy(dst, (jchar const *)utf16.string(), strSize * 2); 340 } 341 sizeCopied = strSize; 342#else 343 sizeCopied = size/2 + size % 2; 344 if (size > bufferSize * 2 || dst == NULL) { 345 newArray = env->NewCharArray(sizeCopied); 346 memcpy(newArray, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size); 347 } else { 348 memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size); 349 } 350#endif 351 } 352 } else if (type == FIELD_TYPE_INTEGER) { 353 int64_t value; 354 if (window->getLong(row, column, &value)) { 355 char buf[32]; 356 int len; 357 snprintf(buf, sizeof(buf), "%lld", value); 358 jchar* dst = env->GetCharArrayElements(buffer, NULL); 359 sizeCopied = charToJchar(buf, dst, bufferSize); 360 } 361 } else if (type == FIELD_TYPE_FLOAT) { 362 double value; 363 if (window->getDouble(row, column, &value)) { 364 char tempbuf[32]; 365 snprintf(tempbuf, sizeof(tempbuf), "%g", value); 366 jchar* dst = env->GetCharArrayElements(buffer, NULL); 367 sizeCopied = charToJchar(tempbuf, dst, bufferSize); 368 } 369 } else if (type == FIELD_TYPE_NULL) { 370 } else if (type == FIELD_TYPE_BLOB) { 371 throw_sqlite3_exception(env, "Unable to convert BLOB to string"); 372 } else { 373 LOGE("Unknown field type %d", type); 374 throw_sqlite3_exception(env, "UNKNOWN type in copyStringToBuffer_native()"); 375 } 376 SET_SIZE_COPIED(env, buf, sizeCopied); 377 env->ReleaseCharArrayElements(buffer, dst, JNI_OK); 378 return newArray; 379} 380 381static jdouble getDouble_native(JNIEnv* env, jobject object, jint row, jint column) 382{ 383 int32_t err; 384 CursorWindow * window = GET_WINDOW(env, object); 385LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); 386 387 field_slot_t field; 388 err = window->read_field_slot(row, column, &field); 389 if (err != 0) { 390 throwExceptionWithRowCol(env, row, column); 391 return 0.0; 392 } 393 394 uint8_t type = field.type; 395 if (type == FIELD_TYPE_FLOAT) { 396 double value; 397 if (window->getDouble(row, column, &value)) { 398 return value; 399 } 400 return 0.0; 401 } else if (type == FIELD_TYPE_STRING) { 402 uint32_t size = field.data.buffer.size; 403 if (size > 0) { 404#if WINDOW_STORAGE_UTF8 405 return strtod((char const *)window->offsetToPtr(field.data.buffer.offset), NULL); 406#else 407 String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2); 408 char const * str = ascii.string(); 409 return strtod(str, NULL); 410#endif 411 } else { 412 return 0.0; 413 } 414 } else if (type == FIELD_TYPE_INTEGER) { 415 int64_t value; 416 if (window->getLong(row, column, &value)) { 417 return (double) value; 418 } 419 return 0.0; 420 } else if (type == FIELD_TYPE_NULL) { 421 return 0.0; 422 } else if (type == FIELD_TYPE_BLOB) { 423 throw_sqlite3_exception(env, "Unable to convert BLOB to double"); 424 return 0.0; 425 } else { 426 throwUnknowTypeException(env, type); 427 return 0.0; 428 } 429} 430 431bool isNull_native(CursorWindow *window, jint row, jint column) 432{ 433 LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window); 434 435 bool isNull; 436 if (window->getNull(row, column, &isNull)) { 437 return isNull; 438 } 439 440 //TODO throw execption? 441 return true; 442} 443 444static jint getNumRows(JNIEnv * env, jobject object) 445{ 446 CursorWindow * window = GET_WINDOW(env, object); 447 return window->getNumRows(); 448} 449 450static jboolean setNumColumns(JNIEnv * env, jobject object, jint columnNum) 451{ 452 CursorWindow * window = GET_WINDOW(env, object); 453 return window->setNumColumns(columnNum); 454} 455 456static jboolean allocRow(JNIEnv * env, jobject object) 457{ 458 CursorWindow * window = GET_WINDOW(env, object); 459 return window->allocRow() != NULL; 460} 461 462static jboolean putBlob_native(JNIEnv * env, jobject object, jbyteArray value, jint row, jint col) 463{ 464 CursorWindow * window = GET_WINDOW(env, object); 465 if (!value) { 466 LOG_WINDOW("How did a null value send to here"); 467 return false; 468 } 469 field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col); 470 if (fieldSlot == NULL) { 471 LOG_WINDOW(" getFieldSlotWithCheck error "); 472 return false; 473 } 474 475 jint len = env->GetArrayLength(value); 476 int offset = window->alloc(len); 477 if (!offset) { 478 LOG_WINDOW("Failed allocating %u bytes", len); 479 return false; 480 } 481 jbyte * bytes = env->GetByteArrayElements(value, NULL); 482 window->copyIn(offset, (uint8_t const *)bytes, len); 483 484 // This must be updated after the call to alloc(), since that 485 // may move the field around in the window 486 fieldSlot->type = FIELD_TYPE_BLOB; 487 fieldSlot->data.buffer.offset = offset; 488 fieldSlot->data.buffer.size = len; 489 env->ReleaseByteArrayElements(value, bytes, JNI_ABORT); 490 LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, col, len, offset); 491 return true; 492} 493 494static jboolean putString_native(JNIEnv * env, jobject object, jstring value, jint row, jint col) 495{ 496 CursorWindow * window = GET_WINDOW(env, object); 497 if (!value) { 498 LOG_WINDOW("How did a null value send to here"); 499 return false; 500 } 501 field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col); 502 if (fieldSlot == NULL) { 503 LOG_WINDOW(" getFieldSlotWithCheck error "); 504 return false; 505 } 506 507#if WINDOW_STORAGE_UTF8 508 int len = env->GetStringUTFLength(value) + 1; 509 char const * valStr = env->GetStringUTFChars(value, NULL); 510#else 511 int len = env->GetStringLength(value); 512 // GetStringLength return number of chars and one char takes 2 bytes 513 len *= 2; 514 const jchar* valStr = env->GetStringChars(value, NULL); 515#endif 516 if (!valStr) { 517 LOG_WINDOW("value can't be transfer to UTFChars"); 518 return false; 519 } 520 521 int offset = window->alloc(len); 522 if (!offset) { 523 LOG_WINDOW("Failed allocating %u bytes", len); 524#if WINDOW_STORAGE_UTF8 525 env->ReleaseStringUTFChars(value, valStr); 526#else 527 env->ReleaseStringChars(value, valStr); 528#endif 529 return false; 530 } 531 532 window->copyIn(offset, (uint8_t const *)valStr, len); 533 534 // This must be updated after the call to alloc(), since that 535 // may move the field around in the window 536 fieldSlot->type = FIELD_TYPE_STRING; 537 fieldSlot->data.buffer.offset = offset; 538 fieldSlot->data.buffer.size = len; 539 540 LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, col, len, offset); 541#if WINDOW_STORAGE_UTF8 542 env->ReleaseStringUTFChars(value, valStr); 543#else 544 env->ReleaseStringChars(value, valStr); 545#endif 546 547 return true; 548} 549 550static jboolean putLong_native(JNIEnv * env, jobject object, jlong value, jint row, jint col) 551{ 552 CursorWindow * window = GET_WINDOW(env, object); 553 if (!window->putLong(row, col, value)) { 554 LOG_WINDOW(" getFieldSlotWithCheck error "); 555 return false; 556 } 557 558 LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, col, value); 559 560 return true; 561} 562 563static jboolean putDouble_native(JNIEnv * env, jobject object, jdouble value, jint row, jint col) 564{ 565 CursorWindow * window = GET_WINDOW(env, object); 566 if (!window->putDouble(row, col, value)) { 567 LOG_WINDOW(" getFieldSlotWithCheck error "); 568 return false; 569 } 570 571 LOG_WINDOW("%d,%d is FLOAT %lf", row, col, value); 572 573 return true; 574} 575 576static jboolean putNull_native(JNIEnv * env, jobject object, jint row, jint col) 577{ 578 CursorWindow * window = GET_WINDOW(env, object); 579 if (!window->putNull(row, col)) { 580 LOG_WINDOW(" getFieldSlotWithCheck error "); 581 return false; 582 } 583 584 LOG_WINDOW("%d,%d is NULL", row, col); 585 586 return true; 587} 588 589// free the last row 590static void freeLastRow(JNIEnv * env, jobject object) { 591 CursorWindow * window = GET_WINDOW(env, object); 592 window->freeLastRow(); 593} 594 595static jint getType_native(JNIEnv* env, jobject object, jint row, jint column) 596{ 597 int32_t err; 598 CursorWindow * window = GET_WINDOW(env, object); 599 LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window); 600 601 if (isNull_native(window, row, column)) { 602 return FIELD_TYPE_NULL; 603 } 604 605 field_slot_t field; 606 err = window->read_field_slot(row, column, &field); 607 if (err != 0) { 608 throwExceptionWithRowCol(env, row, column); 609 return NULL; 610 } 611 612 return field.type; 613} 614 615static JNINativeMethod sMethods[] = 616{ 617 /* name, signature, funcPtr */ 618 {"native_init", "(IZ)V", (void *)native_init_empty}, 619 {"native_init", "(Landroid/os/IBinder;)V", (void *)native_init_memory}, 620 {"native_getBinder", "()Landroid/os/IBinder;", (void *)native_getBinder}, 621 {"native_clear", "()V", (void *)native_clear}, 622 {"close_native", "()V", (void *)native_close}, 623 {"getLong_native", "(II)J", (void *)getLong_native}, 624 {"getBlob_native", "(II)[B", (void *)getBlob_native}, 625 {"getString_native", "(II)Ljava/lang/String;", (void *)getString_native}, 626 {"copyStringToBuffer_native", "(IIILandroid/database/CharArrayBuffer;)[C", (void *)copyStringToBuffer_native}, 627 {"getDouble_native", "(II)D", (void *)getDouble_native}, 628 {"getNumRows_native", "()I", (void *)getNumRows}, 629 {"setNumColumns_native", "(I)Z", (void *)setNumColumns}, 630 {"allocRow_native", "()Z", (void *)allocRow}, 631 {"putBlob_native", "([BII)Z", (void *)putBlob_native}, 632 {"putString_native", "(Ljava/lang/String;II)Z", (void *)putString_native}, 633 {"putLong_native", "(JII)Z", (void *)putLong_native}, 634 {"putDouble_native", "(DII)Z", (void *)putDouble_native}, 635 {"freeLastRow_native", "()V", (void *)freeLastRow}, 636 {"putNull_native", "(II)Z", (void *)putNull_native}, 637 {"getType_native", "(II)I", (void *)getType_native}, 638}; 639 640int register_android_database_CursorWindow(JNIEnv * env) 641{ 642 jclass clazz; 643 644 clazz = env->FindClass("android/database/CursorWindow"); 645 if (clazz == NULL) { 646 LOGE("Can't find android/database/CursorWindow"); 647 return -1; 648 } 649 650 gWindowField = env->GetFieldID(clazz, "nWindow", "I"); 651 652 if (gWindowField == NULL) { 653 LOGE("Error locating fields"); 654 return -1; 655 } 656 657 clazz = env->FindClass("android/database/CharArrayBuffer"); 658 if (clazz == NULL) { 659 LOGE("Can't find android/database/CharArrayBuffer"); 660 return -1; 661 } 662 663 gBufferField = env->GetFieldID(clazz, "data", "[C"); 664 665 if (gBufferField == NULL) { 666 LOGE("Error locating fields data in CharArrayBuffer"); 667 return -1; 668 } 669 670 gSizeCopiedField = env->GetFieldID(clazz, "sizeCopied", "I"); 671 672 if (gSizeCopiedField == NULL) { 673 LOGE("Error locating fields sizeCopied in CharArrayBuffer"); 674 return -1; 675 } 676 677 return AndroidRuntime::registerNativeMethods(env, "android/database/CursorWindow", 678 sMethods, NELEM(sMethods)); 679} 680 681} // namespace android 682