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