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