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