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