• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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