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