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