android_database_CursorWindow.cpp revision 715311fa5aeb39fd0904209e1428a3656c721c3d
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#include <utils/Unicode.h> 28 29#include <stdio.h> 30#include <string.h> 31#include <unistd.h> 32 33#include "binder/CursorWindow.h" 34#include "sqlite3_exception.h" 35#include "android_util_Binder.h" 36 37namespace android { 38 39static struct { 40 jfieldID data; 41 jfieldID sizeCopied; 42} gCharArrayBufferClassInfo; 43 44static jstring gEmptyString; 45 46static void throwExceptionWithRowCol(JNIEnv* env, jint row, jint column) { 47 String8 msg; 48 msg.appendFormat("Couldn't read row %d, col %d from CursorWindow. " 49 "Make sure the Cursor is initialized correctly before accessing data from it.", 50 row, column); 51 jniThrowException(env, "java/lang/IllegalStateException", msg.string()); 52} 53 54static void throwUnknownTypeException(JNIEnv * env, jint type) { 55 String8 msg; 56 msg.appendFormat("UNKNOWN type %d", type); 57 jniThrowException(env, "java/lang/IllegalStateException", msg.string()); 58} 59 60static jint nativeInitializeEmpty(JNIEnv* env, jclass clazz, 61 jint cursorWindowSize, jboolean localOnly) { 62 CursorWindow* window = new CursorWindow(cursorWindowSize); 63 if (!window) { 64 return 0; 65 } 66 if (!window->initBuffer(localOnly)) { 67 delete window; 68 return 0; 69 } 70 71 LOG_WINDOW("nativeInitializeEmpty: window = %p", window); 72 return reinterpret_cast<jint>(window); 73} 74 75static jint nativeInitializeFromBinder(JNIEnv* env, jclass clazz, jobject binderObj) { 76 sp<IMemory> memory = interface_cast<IMemory>(ibinderForJavaObject(env, binderObj)); 77 if (memory == NULL) { 78 jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder"); 79 return 0; 80 } 81 82 CursorWindow* window = new CursorWindow(); 83 if (!window) { 84 return 0; 85 } 86 if (!window->setMemory(memory)) { 87 delete window; 88 return 0; 89 } 90 91 LOG_WINDOW("nativeInitializeFromBinder: numRows = %d, numColumns = %d, window = %p", 92 window->getNumRows(), window->getNumColumns(), window); 93 return reinterpret_cast<jint>(window); 94} 95 96static void nativeDispose(JNIEnv* env, jclass clazz, jint windowPtr) { 97 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 98 if (window) { 99 LOG_WINDOW("Closing window %p", window); 100 delete window; 101 } 102} 103 104static jobject nativeGetBinder(JNIEnv * env, jclass clazz, jint windowPtr) { 105 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 106 if (window) { 107 sp<IMemory> memory = window->getMemory(); 108 if (memory != NULL) { 109 sp<IBinder> binder = memory->asBinder(); 110 return javaObjectForIBinder(env, binder); 111 } 112 } 113 return NULL; 114} 115 116static void nativeClear(JNIEnv * env, jclass clazz, jint windowPtr) { 117 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 118 LOG_WINDOW("Clearing window %p", window); 119 window->clear(); 120} 121 122static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jint windowPtr) { 123 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 124 return window->getNumRows(); 125} 126 127static jboolean nativeSetNumColumns(JNIEnv* env, jclass clazz, jint windowPtr, 128 jint columnNum) { 129 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 130 return window->setNumColumns(columnNum); 131} 132 133static jboolean nativeAllocRow(JNIEnv* env, jclass clazz, jint windowPtr) { 134 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 135 return window->allocRow() != NULL; 136} 137 138static void nativeFreeLastRow(JNIEnv* env, jclass clazz, jint windowPtr) { 139 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 140 window->freeLastRow(); 141} 142 143static jint nativeGetType(JNIEnv* env, jclass clazz, jint windowPtr, 144 jint row, jint column) { 145 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 146 LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window); 147 148 field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); 149 if (!fieldSlot) { 150 // FIXME: This is really broken but we have CTS tests that depend 151 // on this legacy behavior. 152 //throwExceptionWithRowCol(env, row, column); 153 return FIELD_TYPE_NULL; 154 } 155 return fieldSlot->type; 156} 157 158static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jint windowPtr, 159 jint row, jint column) { 160 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 161 LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window); 162 163 field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); 164 if (!fieldSlot) { 165 throwExceptionWithRowCol(env, row, column); 166 return NULL; 167 } 168 169 uint8_t type = fieldSlot->type; 170 if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) { 171 uint32_t size = fieldSlot->data.buffer.size; 172 jbyteArray byteArray = env->NewByteArray(size); 173 if (!byteArray) { 174 env->ExceptionClear(); 175 throw_sqlite3_exception(env, "Native could not create new byte[]"); 176 return NULL; 177 } 178 env->SetByteArrayRegion(byteArray, 0, size, 179 reinterpret_cast<jbyte*>(window->offsetToPtr(fieldSlot->data.buffer.offset))); 180 return byteArray; 181 } else if (type == FIELD_TYPE_INTEGER) { 182 throw_sqlite3_exception(env, "INTEGER data in nativeGetBlob "); 183 } else if (type == FIELD_TYPE_FLOAT) { 184 throw_sqlite3_exception(env, "FLOAT data in nativeGetBlob "); 185 } else if (type == FIELD_TYPE_NULL) { 186 // do nothing 187 } else { 188 throwUnknownTypeException(env, type); 189 } 190 return NULL; 191} 192 193static jstring nativeGetString(JNIEnv* env, jclass clazz, jint windowPtr, 194 jint row, jint column) { 195 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 196 LOG_WINDOW("Getting string for %d,%d from %p", row, column, window); 197 198 field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); 199 if (!fieldSlot) { 200 throwExceptionWithRowCol(env, row, column); 201 return NULL; 202 } 203 204 uint8_t type = fieldSlot->type; 205 if (type == FIELD_TYPE_STRING) { 206 uint32_t size = fieldSlot->data.buffer.size; 207#if WINDOW_STORAGE_UTF8 208 if (size <= 1) { 209 return gEmptyString; 210 } 211 // Convert to UTF-16 here instead of calling NewStringUTF. NewStringUTF 212 // doesn't like UTF-8 strings with high codepoints. It actually expects 213 // Modified UTF-8 with encoded surrogate pairs. 214 String16 utf16(window->getFieldSlotValueString(fieldSlot), size - 1); 215 return env->NewString(reinterpret_cast<const jchar*>(utf16.string()), utf16.size()); 216#else 217 size_t chars = size / sizeof(char16_t); 218 return chars ? env->NewString(reinterpret_cast<jchar*>( 219 window->getFieldSlotValueString(fieldSlot)), chars) 220 : gEmptyString; 221#endif 222 } else if (type == FIELD_TYPE_INTEGER) { 223 int64_t value = window->getFieldSlotValueLong(fieldSlot); 224 char buf[32]; 225 snprintf(buf, sizeof(buf), "%lld", value); 226 return env->NewStringUTF(buf); 227 } else if (type == FIELD_TYPE_FLOAT) { 228 double value = window->getFieldSlotValueDouble(fieldSlot); 229 char buf[32]; 230 snprintf(buf, sizeof(buf), "%g", value); 231 return env->NewStringUTF(buf); 232 } else if (type == FIELD_TYPE_NULL) { 233 return NULL; 234 } else if (type == FIELD_TYPE_BLOB) { 235 throw_sqlite3_exception(env, "Unable to convert BLOB to string"); 236 return NULL; 237 } else { 238 throwUnknownTypeException(env, type); 239 return NULL; 240 } 241} 242 243static jcharArray allocCharArrayBuffer(JNIEnv* env, jobject bufferObj, size_t size) { 244 jcharArray dataObj = jcharArray(env->GetObjectField(bufferObj, 245 gCharArrayBufferClassInfo.data)); 246 if (dataObj && size) { 247 jsize capacity = env->GetArrayLength(dataObj); 248 if (size_t(capacity) < size) { 249 env->DeleteLocalRef(dataObj); 250 dataObj = NULL; 251 } 252 } 253 if (!dataObj) { 254 jsize capacity = size; 255 if (capacity < 64) { 256 capacity = 64; 257 } 258 dataObj = env->NewCharArray(capacity); // might throw OOM 259 if (dataObj) { 260 env->SetObjectField(bufferObj, gCharArrayBufferClassInfo.data, dataObj); 261 } 262 } 263 return dataObj; 264} 265 266static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj, 267 const char* str, size_t len) { 268 ssize_t size = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str), len); 269 if (size < 0) { 270 size = 0; // invalid UTF8 string 271 } 272 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, size); 273 if (dataObj) { 274 if (size) { 275 jchar* data = static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, NULL)); 276 utf8_to_utf16_no_null_terminator(reinterpret_cast<const uint8_t*>(str), len, 277 reinterpret_cast<char16_t*>(data)); 278 env->ReleasePrimitiveArrayCritical(dataObj, data, 0); 279 } 280 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, size); 281 } 282} 283 284#if !WINDOW_STORAGE_UTF8 285static void fillCharArrayBuffer(JNIEnv* env, jobject bufferObj, 286 const char16_t* str, size_t len) { 287 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, len); 288 if (dataObj) { 289 if (len) { 290 jchar* data = static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, NULL)); 291 memcpy(data, str, len * sizeof(jchar)); 292 env->ReleasePrimitiveArrayCritical(dataObj, data, 0); 293 } 294 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, len); 295 } 296} 297#endif 298 299static void clearCharArrayBuffer(JNIEnv* env, jobject bufferObj) { 300 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, 0); 301 if (dataObj) { 302 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, 0); 303 } 304} 305 306static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jint windowPtr, 307 jint row, jint column, jobject bufferObj) { 308 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 309 LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); 310 311 field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); 312 if (!fieldSlot) { 313 throwExceptionWithRowCol(env, row, column); 314 return; 315 } 316 317 uint8_t type = fieldSlot->type; 318 if (type == FIELD_TYPE_STRING) { 319 uint32_t size = fieldSlot->data.buffer.size; 320#if WINDOW_STORAGE_UTF8 321 if (size > 1) { 322 fillCharArrayBufferUTF(env, bufferObj, 323 window->getFieldSlotValueString(fieldSlot), size - 1); 324 } else { 325 clearCharArrayBuffer(env, bufferObj); 326 } 327#else 328 size_t chars = size / sizeof(char16_t); 329 if (chars) { 330 fillCharArrayBuffer(env, bufferObj, 331 window->getFieldSlotValueString(fieldSlot), chars); 332 } else { 333 clearCharArrayBuffer(env, bufferObj); 334 } 335#endif 336 } else if (type == FIELD_TYPE_INTEGER) { 337 int64_t value = window->getFieldSlotValueLong(fieldSlot); 338 char buf[32]; 339 snprintf(buf, sizeof(buf), "%lld", value); 340 fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); 341 } else if (type == FIELD_TYPE_FLOAT) { 342 double value = window->getFieldSlotValueDouble(fieldSlot); 343 char buf[32]; 344 snprintf(buf, sizeof(buf), "%g", value); 345 fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); 346 } else if (type == FIELD_TYPE_NULL) { 347 clearCharArrayBuffer(env, bufferObj); 348 } else if (type == FIELD_TYPE_BLOB) { 349 throw_sqlite3_exception(env, "Unable to convert BLOB to string"); 350 } else { 351 throwUnknownTypeException(env, type); 352 } 353} 354 355static jlong nativeGetLong(JNIEnv* env, jclass clazz, jint windowPtr, 356 jint row, jint column) { 357 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 358 LOG_WINDOW("Getting long for %d,%d from %p", row, column, window); 359 360 field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); 361 if (!fieldSlot) { 362 throwExceptionWithRowCol(env, row, column); 363 return 0; 364 } 365 366 uint8_t type = fieldSlot->type; 367 if (type == FIELD_TYPE_INTEGER) { 368 return window->getFieldSlotValueLong(fieldSlot); 369 } else if (type == FIELD_TYPE_STRING) { 370 uint32_t size = fieldSlot->data.buffer.size; 371#if WINDOW_STORAGE_UTF8 372 return size > 1 ? strtoll(window->getFieldSlotValueString(fieldSlot), NULL, 0) : 0L; 373#else 374 size_t chars = size / sizeof(char16_t); 375 return chars ? strtoll(String8(window->getFieldSlotValueString(fieldSlot), chars) 376 .string(), NULL, 0) : 0L; 377#endif 378 } else if (type == FIELD_TYPE_FLOAT) { 379 return jlong(window->getFieldSlotValueDouble(fieldSlot)); 380 } else if (type == FIELD_TYPE_NULL) { 381 return 0; 382 } else if (type == FIELD_TYPE_BLOB) { 383 throw_sqlite3_exception(env, "Unable to convert BLOB to long"); 384 return 0; 385 } else { 386 throwUnknownTypeException(env, type); 387 return 0; 388 } 389} 390 391static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jint windowPtr, 392 jint row, jint column) { 393 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 394 LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); 395 396 field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); 397 if (!fieldSlot) { 398 throwExceptionWithRowCol(env, row, column); 399 return 0.0; 400 } 401 402 uint8_t type = fieldSlot->type; 403 if (type == FIELD_TYPE_FLOAT) { 404 return window->getFieldSlotValueDouble(fieldSlot); 405 } else if (type == FIELD_TYPE_STRING) { 406 uint32_t size = fieldSlot->data.buffer.size; 407#if WINDOW_STORAGE_UTF8 408 return size > 1 ? strtod(window->getFieldSlotValueString(fieldSlot), NULL) : 0.0; 409#else 410 size_t chars = size / sizeof(char16_t); 411 return chars ? strtod(String8(window->getFieldSlotValueString(fieldSlot), chars) 412 .string(), NULL) : 0.0; 413#endif 414 } else if (type == FIELD_TYPE_INTEGER) { 415 return jdouble(window->getFieldSlotValueLong(fieldSlot)); 416 } else if (type == FIELD_TYPE_NULL) { 417 return 0.0; 418 } else if (type == FIELD_TYPE_BLOB) { 419 throw_sqlite3_exception(env, "Unable to convert BLOB to double"); 420 return 0.0; 421 } else { 422 throwUnknownTypeException(env, type); 423 return 0.0; 424 } 425} 426 427static jboolean nativePutBlob(JNIEnv* env, jclass clazz, jint windowPtr, 428 jbyteArray valueObj, jint row, jint column) { 429 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 430 field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, column); 431 if (fieldSlot == NULL) { 432 LOG_WINDOW(" getFieldSlotWithCheck error "); 433 return false; 434 } 435 436 jsize len = env->GetArrayLength(valueObj); 437 uint32_t offset = window->alloc(len); 438 if (!offset) { 439 LOG_WINDOW("Failed allocating %u bytes", len); 440 return false; 441 } 442 443 void* value = env->GetPrimitiveArrayCritical(valueObj, NULL); 444 window->copyIn(offset, static_cast<const uint8_t*>(value), len); 445 env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT); 446 447 fieldSlot->type = FIELD_TYPE_BLOB; 448 fieldSlot->data.buffer.offset = offset; 449 fieldSlot->data.buffer.size = len; 450 LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, column, len, offset); 451 return true; 452} 453 454static jboolean nativePutString(JNIEnv* env, jclass clazz, jint windowPtr, 455 jstring valueObj, jint row, jint column) { 456 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 457 field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, column); 458 if (fieldSlot == NULL) { 459 LOG_WINDOW(" getFieldSlotWithCheck error "); 460 return false; 461 } 462 463#if WINDOW_STORAGE_UTF8 464 size_t size = env->GetStringUTFLength(valueObj) + 1; 465 const char* valueStr = env->GetStringUTFChars(valueObj, NULL); 466#else 467 size_t size = env->GetStringLength(valueObj) * sizeof(jchar); 468 const jchar* valueStr = env->GetStringChars(valueObj, NULL); 469#endif 470 if (!valueStr) { 471 LOG_WINDOW("value can't be transfer to UTFChars"); 472 return false; 473 } 474 475 uint32_t offset = window->alloc(size); 476 if (!offset) { 477 LOG_WINDOW("Failed allocating %u bytes", size); 478#if WINDOW_STORAGE_UTF8 479 env->ReleaseStringUTFChars(valueObj, valueStr); 480#else 481 env->ReleaseStringChars(valueObj, valueStr); 482#endif 483 return false; 484 } 485 486 window->copyIn(offset, reinterpret_cast<const uint8_t*>(valueStr), size); 487 488#if WINDOW_STORAGE_UTF8 489 env->ReleaseStringUTFChars(valueObj, valueStr); 490#else 491 env->ReleaseStringChars(valueObj, valueStr); 492#endif 493 494 fieldSlot->type = FIELD_TYPE_STRING; 495 fieldSlot->data.buffer.offset = offset; 496 fieldSlot->data.buffer.size = size; 497 LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, column, size, offset); 498 return true; 499} 500 501static jboolean nativePutLong(JNIEnv* env, jclass clazz, jint windowPtr, 502 jlong value, jint row, jint column) { 503 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 504 if (!window->putLong(row, column, value)) { 505 LOG_WINDOW(" getFieldSlotWithCheck error "); 506 return false; 507 } 508 509 LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, column, value); 510 return true; 511} 512 513static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jint windowPtr, 514 jdouble value, jint row, jint column) { 515 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 516 if (!window->putDouble(row, column, value)) { 517 LOG_WINDOW(" getFieldSlotWithCheck error "); 518 return false; 519 } 520 521 LOG_WINDOW("%d,%d is FLOAT %lf", row, column, value); 522 return true; 523} 524 525static jboolean nativePutNull(JNIEnv* env, jclass clazz, jint windowPtr, 526 jint row, jint column) { 527 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 528 if (!window->putNull(row, column)) { 529 LOG_WINDOW(" getFieldSlotWithCheck error "); 530 return false; 531 } 532 533 LOG_WINDOW("%d,%d is NULL", row, column); 534 return true; 535} 536 537static JNINativeMethod sMethods[] = 538{ 539 /* name, signature, funcPtr */ 540 { "nativeInitializeEmpty", "(IZ)I", 541 (void*)nativeInitializeEmpty }, 542 { "nativeInitializeFromBinder", "(Landroid/os/IBinder;)I", 543 (void*)nativeInitializeFromBinder }, 544 { "nativeDispose", "(I)V", 545 (void*)nativeDispose }, 546 { "nativeGetBinder", "(I)Landroid/os/IBinder;", 547 (void*)nativeGetBinder }, 548 { "nativeClear", "(I)V", 549 (void*)nativeClear }, 550 { "nativeGetNumRows", "(I)I", 551 (void*)nativeGetNumRows }, 552 { "nativeSetNumColumns", "(II)Z", 553 (void*)nativeSetNumColumns }, 554 { "nativeAllocRow", "(I)Z", 555 (void*)nativeAllocRow }, 556 { "nativeFreeLastRow", "(I)V", 557 (void*)nativeFreeLastRow }, 558 { "nativeGetType", "(III)I", 559 (void*)nativeGetType }, 560 { "nativeGetBlob", "(III)[B", 561 (void*)nativeGetBlob }, 562 { "nativeGetString", "(III)Ljava/lang/String;", 563 (void*)nativeGetString }, 564 { "nativeGetLong", "(III)J", 565 (void*)nativeGetLong }, 566 { "nativeGetDouble", "(III)D", 567 (void*)nativeGetDouble }, 568 { "nativeCopyStringToBuffer", "(IIILandroid/database/CharArrayBuffer;)V", 569 (void*)nativeCopyStringToBuffer }, 570 { "nativePutBlob", "(I[BII)Z", 571 (void*)nativePutBlob }, 572 { "nativePutString", "(ILjava/lang/String;II)Z", 573 (void*)nativePutString }, 574 { "nativePutLong", "(IJII)Z", 575 (void*)nativePutLong }, 576 { "nativePutDouble", "(IDII)Z", 577 (void*)nativePutDouble }, 578 { "nativePutNull", "(III)Z", 579 (void*)nativePutNull }, 580}; 581 582#define FIND_CLASS(var, className) \ 583 var = env->FindClass(className); \ 584 LOG_FATAL_IF(! var, "Unable to find class " className); 585 586#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ 587 var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ 588 LOG_FATAL_IF(! var, "Unable to find field " fieldName); 589 590int register_android_database_CursorWindow(JNIEnv * env) 591{ 592 jclass clazz; 593 FIND_CLASS(clazz, "android/database/CharArrayBuffer"); 594 595 GET_FIELD_ID(gCharArrayBufferClassInfo.data, clazz, 596 "data", "[C"); 597 GET_FIELD_ID(gCharArrayBufferClassInfo.sizeCopied, clazz, 598 "sizeCopied", "I"); 599 600 gEmptyString = jstring(env->NewGlobalRef(env->NewStringUTF(""))); 601 LOG_FATAL_IF(!gEmptyString, "Unable to create empty string"); 602 603 return AndroidRuntime::registerNativeMethods(env, "android/database/CursorWindow", 604 sMethods, NELEM(sMethods)); 605} 606 607} // namespace android 608