android_database_CursorWindow.cpp revision d0ff68da6a606602235fb8749473999e3d1bde53
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 return size > 1 ? env->NewStringUTF(window->getFieldSlotValueString(fieldSlot)) 209 : gEmptyString; 210#else 211 size_t chars = size / sizeof(char16_t); 212 return chars ? env->NewString(reinterpret_cast<jchar*>( 213 window->getFieldSlotValueString(fieldSlot)), chars) 214 : gEmptyString; 215#endif 216 } else if (type == FIELD_TYPE_INTEGER) { 217 int64_t value = window->getFieldSlotValueLong(fieldSlot); 218 char buf[32]; 219 snprintf(buf, sizeof(buf), "%lld", value); 220 return env->NewStringUTF(buf); 221 } else if (type == FIELD_TYPE_FLOAT) { 222 double value = window->getFieldSlotValueDouble(fieldSlot); 223 char buf[32]; 224 snprintf(buf, sizeof(buf), "%g", value); 225 return env->NewStringUTF(buf); 226 } else if (type == FIELD_TYPE_NULL) { 227 return NULL; 228 } else if (type == FIELD_TYPE_BLOB) { 229 throw_sqlite3_exception(env, "Unable to convert BLOB to string"); 230 return NULL; 231 } else { 232 throwUnknownTypeException(env, type); 233 return NULL; 234 } 235} 236 237static jcharArray allocCharArrayBuffer(JNIEnv* env, jobject bufferObj, size_t size) { 238 jcharArray dataObj = jcharArray(env->GetObjectField(bufferObj, 239 gCharArrayBufferClassInfo.data)); 240 if (dataObj && size) { 241 jsize capacity = env->GetArrayLength(dataObj); 242 if (size_t(capacity) < size) { 243 env->DeleteLocalRef(dataObj); 244 dataObj = NULL; 245 } 246 } 247 if (!dataObj) { 248 jsize capacity = size; 249 if (capacity < 64) { 250 capacity = 64; 251 } 252 dataObj = env->NewCharArray(capacity); // might throw OOM 253 if (dataObj) { 254 env->SetObjectField(bufferObj, gCharArrayBufferClassInfo.data, dataObj); 255 } 256 } 257 return dataObj; 258} 259 260static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj, 261 const char* str, size_t len) { 262 ssize_t size = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str), len); 263 if (size < 0) { 264 size = 0; // invalid UTF8 string 265 } 266 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, size); 267 if (dataObj) { 268 if (size) { 269 jchar* data = static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, NULL)); 270 utf8_to_utf16_no_null_terminator(reinterpret_cast<const uint8_t*>(str), len, 271 reinterpret_cast<char16_t*>(data)); 272 env->ReleasePrimitiveArrayCritical(dataObj, data, 0); 273 } 274 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, size); 275 } 276} 277 278#if !WINDOW_STORAGE_UTF8 279static void fillCharArrayBuffer(JNIEnv* env, jobject bufferObj, 280 const char16_t* str, size_t len) { 281 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, len); 282 if (dataObj) { 283 if (len) { 284 jchar* data = static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, NULL)); 285 memcpy(data, str, len * sizeof(jchar)); 286 env->ReleasePrimitiveArrayCritical(dataObj, data, 0); 287 } 288 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, len); 289 } 290} 291#endif 292 293static void clearCharArrayBuffer(JNIEnv* env, jobject bufferObj) { 294 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, 0); 295 if (dataObj) { 296 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, 0); 297 } 298} 299 300static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jint windowPtr, 301 jint row, jint column, jobject bufferObj) { 302 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 303 LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); 304 305 field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); 306 if (!fieldSlot) { 307 throwExceptionWithRowCol(env, row, column); 308 return; 309 } 310 311 uint8_t type = fieldSlot->type; 312 if (type == FIELD_TYPE_STRING) { 313 uint32_t size = fieldSlot->data.buffer.size; 314#if WINDOW_STORAGE_UTF8 315 if (size > 1) { 316 fillCharArrayBufferUTF(env, bufferObj, 317 window->getFieldSlotValueString(fieldSlot), size - 1); 318 } else { 319 clearCharArrayBuffer(env, bufferObj); 320 } 321#else 322 size_t chars = size / sizeof(char16_t); 323 if (chars) { 324 fillCharArrayBuffer(env, bufferObj, 325 window->getFieldSlotValueString(fieldSlot), chars); 326 } else { 327 clearCharArrayBuffer(env, bufferObj); 328 } 329#endif 330 } else if (type == FIELD_TYPE_INTEGER) { 331 int64_t value = window->getFieldSlotValueLong(fieldSlot); 332 char buf[32]; 333 snprintf(buf, sizeof(buf), "%lld", value); 334 fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); 335 } else if (type == FIELD_TYPE_FLOAT) { 336 double value = window->getFieldSlotValueDouble(fieldSlot); 337 char buf[32]; 338 snprintf(buf, sizeof(buf), "%g", value); 339 fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); 340 } else if (type == FIELD_TYPE_NULL) { 341 clearCharArrayBuffer(env, bufferObj); 342 } else if (type == FIELD_TYPE_BLOB) { 343 throw_sqlite3_exception(env, "Unable to convert BLOB to string"); 344 } else { 345 throwUnknownTypeException(env, type); 346 } 347} 348 349static jlong nativeGetLong(JNIEnv* env, jclass clazz, jint windowPtr, 350 jint row, jint column) { 351 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 352 LOG_WINDOW("Getting long for %d,%d from %p", row, column, window); 353 354 field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); 355 if (!fieldSlot) { 356 throwExceptionWithRowCol(env, row, column); 357 return 0; 358 } 359 360 uint8_t type = fieldSlot->type; 361 if (type == FIELD_TYPE_INTEGER) { 362 return window->getFieldSlotValueLong(fieldSlot); 363 } else if (type == FIELD_TYPE_STRING) { 364 uint32_t size = fieldSlot->data.buffer.size; 365#if WINDOW_STORAGE_UTF8 366 return size > 1 ? strtoll(window->getFieldSlotValueString(fieldSlot), NULL, 0) : 0L; 367#else 368 size_t chars = size / sizeof(char16_t); 369 return chars ? strtoll(String8(window->getFieldSlotValueString(fieldSlot), chars) 370 .string(), NULL, 0) : 0L; 371#endif 372 } else if (type == FIELD_TYPE_FLOAT) { 373 return jlong(window->getFieldSlotValueDouble(fieldSlot)); 374 } else if (type == FIELD_TYPE_NULL) { 375 return 0; 376 } else if (type == FIELD_TYPE_BLOB) { 377 throw_sqlite3_exception(env, "Unable to convert BLOB to long"); 378 return 0; 379 } else { 380 throwUnknownTypeException(env, type); 381 return 0; 382 } 383} 384 385static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jint windowPtr, 386 jint row, jint column) { 387 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 388 LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); 389 390 field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); 391 if (!fieldSlot) { 392 throwExceptionWithRowCol(env, row, column); 393 return 0.0; 394 } 395 396 uint8_t type = fieldSlot->type; 397 if (type == FIELD_TYPE_FLOAT) { 398 return window->getFieldSlotValueDouble(fieldSlot); 399 } else if (type == FIELD_TYPE_STRING) { 400 uint32_t size = fieldSlot->data.buffer.size; 401#if WINDOW_STORAGE_UTF8 402 return size > 1 ? strtod(window->getFieldSlotValueString(fieldSlot), NULL) : 0.0; 403#else 404 size_t chars = size / sizeof(char16_t); 405 return chars ? strtod(String8(window->getFieldSlotValueString(fieldSlot), chars) 406 .string(), NULL) : 0.0; 407#endif 408 } else if (type == FIELD_TYPE_INTEGER) { 409 return jdouble(window->getFieldSlotValueLong(fieldSlot)); 410 } else if (type == FIELD_TYPE_NULL) { 411 return 0.0; 412 } else if (type == FIELD_TYPE_BLOB) { 413 throw_sqlite3_exception(env, "Unable to convert BLOB to double"); 414 return 0.0; 415 } else { 416 throwUnknownTypeException(env, type); 417 return 0.0; 418 } 419} 420 421static jboolean nativePutBlob(JNIEnv* env, jclass clazz, jint windowPtr, 422 jbyteArray valueObj, jint row, jint column) { 423 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 424 field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, column); 425 if (fieldSlot == NULL) { 426 LOG_WINDOW(" getFieldSlotWithCheck error "); 427 return false; 428 } 429 430 jsize len = env->GetArrayLength(valueObj); 431 uint32_t offset = window->alloc(len); 432 if (!offset) { 433 LOG_WINDOW("Failed allocating %u bytes", len); 434 return false; 435 } 436 437 void* value = env->GetPrimitiveArrayCritical(valueObj, NULL); 438 window->copyIn(offset, static_cast<const uint8_t*>(value), len); 439 env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT); 440 441 fieldSlot->type = FIELD_TYPE_BLOB; 442 fieldSlot->data.buffer.offset = offset; 443 fieldSlot->data.buffer.size = len; 444 LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, column, len, offset); 445 return true; 446} 447 448static jboolean nativePutString(JNIEnv* env, jclass clazz, jint windowPtr, 449 jstring valueObj, jint row, jint column) { 450 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 451 field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, column); 452 if (fieldSlot == NULL) { 453 LOG_WINDOW(" getFieldSlotWithCheck error "); 454 return false; 455 } 456 457#if WINDOW_STORAGE_UTF8 458 size_t size = env->GetStringUTFLength(valueObj) + 1; 459 const char* valueStr = env->GetStringUTFChars(valueObj, NULL); 460#else 461 size_t size = env->GetStringLength(valueObj) * sizeof(jchar); 462 const jchar* valueStr = env->GetStringChars(valueObj, NULL); 463#endif 464 if (!valueStr) { 465 LOG_WINDOW("value can't be transfer to UTFChars"); 466 return false; 467 } 468 469 uint32_t offset = window->alloc(size); 470 if (!offset) { 471 LOG_WINDOW("Failed allocating %u bytes", size); 472#if WINDOW_STORAGE_UTF8 473 env->ReleaseStringUTFChars(valueObj, valueStr); 474#else 475 env->ReleaseStringChars(valueObj, valueStr); 476#endif 477 return false; 478 } 479 480 window->copyIn(offset, reinterpret_cast<const uint8_t*>(valueStr), size); 481 482#if WINDOW_STORAGE_UTF8 483 env->ReleaseStringUTFChars(valueObj, valueStr); 484#else 485 env->ReleaseStringChars(valueObj, valueStr); 486#endif 487 488 fieldSlot->type = FIELD_TYPE_STRING; 489 fieldSlot->data.buffer.offset = offset; 490 fieldSlot->data.buffer.size = size; 491 LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, column, size, offset); 492 return true; 493} 494 495static jboolean nativePutLong(JNIEnv* env, jclass clazz, jint windowPtr, 496 jlong value, jint row, jint column) { 497 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 498 if (!window->putLong(row, column, value)) { 499 LOG_WINDOW(" getFieldSlotWithCheck error "); 500 return false; 501 } 502 503 LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, column, value); 504 return true; 505} 506 507static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jint windowPtr, 508 jdouble value, jint row, jint column) { 509 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 510 if (!window->putDouble(row, column, value)) { 511 LOG_WINDOW(" getFieldSlotWithCheck error "); 512 return false; 513 } 514 515 LOG_WINDOW("%d,%d is FLOAT %lf", row, column, value); 516 return true; 517} 518 519static jboolean nativePutNull(JNIEnv* env, jclass clazz, jint windowPtr, 520 jint row, jint column) { 521 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 522 if (!window->putNull(row, column)) { 523 LOG_WINDOW(" getFieldSlotWithCheck error "); 524 return false; 525 } 526 527 LOG_WINDOW("%d,%d is NULL", row, column); 528 return true; 529} 530 531static JNINativeMethod sMethods[] = 532{ 533 /* name, signature, funcPtr */ 534 { "nativeInitializeEmpty", "(IZ)I", 535 (void*)nativeInitializeEmpty }, 536 { "nativeInitializeFromBinder", "(Landroid/os/IBinder;)I", 537 (void*)nativeInitializeFromBinder }, 538 { "nativeDispose", "(I)V", 539 (void*)nativeDispose }, 540 { "nativeGetBinder", "(I)Landroid/os/IBinder;", 541 (void*)nativeGetBinder }, 542 { "nativeClear", "(I)V", 543 (void*)nativeClear }, 544 { "nativeGetNumRows", "(I)I", 545 (void*)nativeGetNumRows }, 546 { "nativeSetNumColumns", "(II)Z", 547 (void*)nativeSetNumColumns }, 548 { "nativeAllocRow", "(I)Z", 549 (void*)nativeAllocRow }, 550 { "nativeFreeLastRow", "(I)V", 551 (void*)nativeFreeLastRow }, 552 { "nativeGetType", "(III)I", 553 (void*)nativeGetType }, 554 { "nativeGetBlob", "(III)[B", 555 (void*)nativeGetBlob }, 556 { "nativeGetString", "(III)Ljava/lang/String;", 557 (void*)nativeGetString }, 558 { "nativeGetLong", "(III)J", 559 (void*)nativeGetLong }, 560 { "nativeGetDouble", "(III)D", 561 (void*)nativeGetDouble }, 562 { "nativeCopyStringToBuffer", "(IIILandroid/database/CharArrayBuffer;)V", 563 (void*)nativeCopyStringToBuffer }, 564 { "nativePutBlob", "(I[BII)Z", 565 (void*)nativePutBlob }, 566 { "nativePutString", "(ILjava/lang/String;II)Z", 567 (void*)nativePutString }, 568 { "nativePutLong", "(IJII)Z", 569 (void*)nativePutLong }, 570 { "nativePutDouble", "(IDII)Z", 571 (void*)nativePutDouble }, 572 { "nativePutNull", "(III)Z", 573 (void*)nativePutNull }, 574}; 575 576#define FIND_CLASS(var, className) \ 577 var = env->FindClass(className); \ 578 LOG_FATAL_IF(! var, "Unable to find class " className); 579 580#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ 581 var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ 582 LOG_FATAL_IF(! var, "Unable to find field " fieldName); 583 584int register_android_database_CursorWindow(JNIEnv * env) 585{ 586 jclass clazz; 587 FIND_CLASS(clazz, "android/database/CharArrayBuffer"); 588 589 GET_FIELD_ID(gCharArrayBufferClassInfo.data, clazz, 590 "data", "[C"); 591 GET_FIELD_ID(gCharArrayBufferClassInfo.sizeCopied, clazz, 592 "sizeCopied", "I"); 593 594 gEmptyString = jstring(env->NewGlobalRef(env->NewStringUTF(""))); 595 LOG_FATAL_IF(!gEmptyString, "Unable to create empty string"); 596 597 return AndroidRuntime::registerNativeMethods(env, "android/database/CursorWindow", 598 sMethods, NELEM(sMethods)); 599} 600 601} // namespace android 602