blob: 86cda44557343f817c2ca09a1c12b7ae09522054 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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"
Amith Yamasani0903ec52016-08-26 09:22:36 -070019#define LOG_NDEBUG 0
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020
Mark Salyzyn85394032014-04-16 10:28:37 -070021#include <inttypes.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022#include <jni.h>
Steven Moreland2279b252017-07-19 09:50:45 -070023#include <nativehelper/JNIHelp.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024#include <android_runtime/AndroidRuntime.h>
25
26#include <utils/Log.h>
27#include <utils/String8.h>
28#include <utils/String16.h>
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070029#include <utils/Unicode.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030
31#include <stdio.h>
32#include <string.h>
33#include <unistd.h>
Amith Yamasani0903ec52016-08-26 09:22:36 -070034#include <sys/types.h>
35#include <dirent.h>
36
37#undef LOG_NDEBUG
38#define LOG_NDEBUG 1
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039
Mathias Agopian49d2b182012-02-27 18:11:20 -080040#include <androidfw/CursorWindow.h>
Jeff Sharkeyd84e1ce2012-03-06 18:26:19 -080041#include "android_os_Parcel.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042#include "android_util_Binder.h"
Jeff Browne5360fb2011-10-31 17:48:13 -070043#include "android_database_SQLiteCommon.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044
Andreas Gampe987f79f2014-11-18 17:29:46 -080045#include "core_jni_helpers.h"
46
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047namespace android {
48
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070049static struct {
50 jfieldID data;
51 jfieldID sizeCopied;
52} gCharArrayBufferClassInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070054static jstring gEmptyString;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070056static 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());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062}
63
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070064static 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}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069
Amith Yamasani0903ec52016-08-26 09:22:36 -070070static 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
Ashok Bhat738702d2014-01-02 13:42:56 +000086static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, jint cursorWindowSize) {
Jeff Brown0cde89f2011-10-10 14:50:10 -070087 String8 name;
Jeff Brown5a05c232012-01-12 12:04:22 -080088 const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
89 name.setTo(nameStr);
90 env->ReleaseStringUTFChars(nameObj, nameStr);
Jeff Brown0cde89f2011-10-10 14:50:10 -070091
92 CursorWindow* window;
Jeff Brown5e5d6d82011-10-12 15:41:34 -070093 status_t status = CursorWindow::create(name, cursorWindowSize, &window);
Jeff Brown0cde89f2011-10-10 14:50:10 -070094 if (status || !window) {
Steve Block3762c312012-01-06 19:20:56 +000095 ALOGE("Could not allocate CursorWindow '%s' of size %d due to error %d.",
Jeff Brown0cde89f2011-10-10 14:50:10 -070096 name.string(), cursorWindowSize, status);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070097 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 }
99
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700100 LOG_WINDOW("nativeInitializeEmpty: window = %p", window);
Ashok Bhat738702d2014-01-02 13:42:56 +0000101 return reinterpret_cast<jlong>(window);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102}
103
Ashok Bhat738702d2014-01-02 13:42:56 +0000104static jlong nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
Jeff Brown0cde89f2011-10-10 14:50:10 -0700105 Parcel* parcel = parcelForJavaObject(env, parcelObj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106
Jeff Brown0cde89f2011-10-10 14:50:10 -0700107 CursorWindow* window;
108 status_t status = CursorWindow::createFromParcel(parcel, &window);
109 if (status || !window) {
Amith Yamasani0903ec52016-08-26 09:22:36 -0700110 ALOGE("Could not create CursorWindow from Parcel due to error %d, process fd count=%d",
111 status, getFdCount());
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700112 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 }
114
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700115 LOG_WINDOW("nativeInitializeFromBinder: numRows = %d, numColumns = %d, window = %p",
116 window->getNumRows(), window->getNumColumns(), window);
Ashok Bhat738702d2014-01-02 13:42:56 +0000117 return reinterpret_cast<jlong>(window);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118}
119
Ashok Bhat738702d2014-01-02 13:42:56 +0000120static void nativeDispose(JNIEnv* env, jclass clazz, jlong windowPtr) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700121 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
122 if (window) {
123 LOG_WINDOW("Closing window %p", window);
124 delete window;
125 }
126}
127
Ashok Bhat738702d2014-01-02 13:42:56 +0000128static jstring nativeGetName(JNIEnv* env, jclass clazz, jlong windowPtr) {
Jeff Brown650de3d2011-10-27 14:52:28 -0700129 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
130 return env->NewStringUTF(window->name().string());
131}
132
Ashok Bhat738702d2014-01-02 13:42:56 +0000133static void nativeWriteToParcel(JNIEnv * env, jclass clazz, jlong windowPtr,
Jeff Brown0cde89f2011-10-10 14:50:10 -0700134 jobject parcelObj) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700135 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700136 Parcel* parcel = parcelForJavaObject(env, parcelObj);
137
138 status_t status = window->writeToParcel(parcel);
139 if (status) {
140 String8 msg;
141 msg.appendFormat("Could not write CursorWindow to Parcel due to error %d.", status);
142 jniThrowRuntimeException(env, msg.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144}
145
Ashok Bhat738702d2014-01-02 13:42:56 +0000146static void nativeClear(JNIEnv * env, jclass clazz, jlong windowPtr) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700147 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
148 LOG_WINDOW("Clearing window %p", window);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700149 status_t status = window->clear();
150 if (status) {
151 LOG_WINDOW("Could not clear window. error=%d", status);
152 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153}
154
Ashok Bhat738702d2014-01-02 13:42:56 +0000155static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jlong windowPtr) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700156 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
157 return window->getNumRows();
158}
159
Ashok Bhat738702d2014-01-02 13:42:56 +0000160static jboolean nativeSetNumColumns(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700161 jint columnNum) {
162 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700163 status_t status = window->setNumColumns(columnNum);
164 return status == OK;
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700165}
166
Ashok Bhat738702d2014-01-02 13:42:56 +0000167static jboolean nativeAllocRow(JNIEnv* env, jclass clazz, jlong windowPtr) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700168 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700169 status_t status = window->allocRow();
170 return status == OK;
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700171}
172
Ashok Bhat738702d2014-01-02 13:42:56 +0000173static void nativeFreeLastRow(JNIEnv* env, jclass clazz, jlong windowPtr) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700174 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
175 window->freeLastRow();
176}
177
Ashok Bhat738702d2014-01-02 13:42:56 +0000178static jint nativeGetType(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700179 jint row, jint column) {
180 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
181 LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window);
182
Jeff Brown0cde89f2011-10-10 14:50:10 -0700183 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700184 if (!fieldSlot) {
Jeff Brownaa32c302011-10-07 13:15:59 -0700185 // FIXME: This is really broken but we have CTS tests that depend
186 // on this legacy behavior.
187 //throwExceptionWithRowCol(env, row, column);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700188 return CursorWindow::FIELD_TYPE_NULL;
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700189 }
Jeff Brown0cde89f2011-10-10 14:50:10 -0700190 return window->getFieldSlotType(fieldSlot);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700191}
192
Ashok Bhat738702d2014-01-02 13:42:56 +0000193static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700194 jint row, jint column) {
195 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
196 LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window);
197
Jeff Brown0cde89f2011-10-10 14:50:10 -0700198 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700199 if (!fieldSlot) {
200 throwExceptionWithRowCol(env, row, column);
201 return NULL;
202 }
203
Jeff Brown0cde89f2011-10-10 14:50:10 -0700204 int32_t type = window->getFieldSlotType(fieldSlot);
205 if (type == CursorWindow::FIELD_TYPE_BLOB || type == CursorWindow::FIELD_TYPE_STRING) {
206 size_t size;
207 const void* value = window->getFieldSlotValueBlob(fieldSlot, &size);
Fyodor Kupoloveb6de6f2017-02-22 14:12:50 -0800208 if (!value) {
209 throw_sqlite3_exception(env, "Native could not read blob slot");
210 return NULL;
211 }
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700212 jbyteArray byteArray = env->NewByteArray(size);
213 if (!byteArray) {
214 env->ExceptionClear();
215 throw_sqlite3_exception(env, "Native could not create new byte[]");
216 return NULL;
217 }
Jeff Brown0cde89f2011-10-10 14:50:10 -0700218 env->SetByteArrayRegion(byteArray, 0, size, static_cast<const jbyte*>(value));
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700219 return byteArray;
Jeff Brown0cde89f2011-10-10 14:50:10 -0700220 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700221 throw_sqlite3_exception(env, "INTEGER data in nativeGetBlob ");
Jeff Brown0cde89f2011-10-10 14:50:10 -0700222 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700223 throw_sqlite3_exception(env, "FLOAT data in nativeGetBlob ");
Jeff Brown0cde89f2011-10-10 14:50:10 -0700224 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700225 // do nothing
226 } else {
227 throwUnknownTypeException(env, type);
228 }
229 return NULL;
230}
231
Ashok Bhat738702d2014-01-02 13:42:56 +0000232static jstring nativeGetString(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700233 jint row, jint column) {
234 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
235 LOG_WINDOW("Getting string for %d,%d from %p", row, column, window);
236
Jeff Brown0cde89f2011-10-10 14:50:10 -0700237 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700238 if (!fieldSlot) {
239 throwExceptionWithRowCol(env, row, column);
240 return NULL;
241 }
242
Jeff Brown0cde89f2011-10-10 14:50:10 -0700243 int32_t type = window->getFieldSlotType(fieldSlot);
244 if (type == CursorWindow::FIELD_TYPE_STRING) {
245 size_t sizeIncludingNull;
246 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
Fyodor Kupoloveb6de6f2017-02-22 14:12:50 -0800247 if (!value) {
248 throw_sqlite3_exception(env, "Native could not read string slot");
249 return NULL;
250 }
Jeff Brown0cde89f2011-10-10 14:50:10 -0700251 if (sizeIncludingNull <= 1) {
Jeff Brown715311f2011-10-07 14:17:09 -0700252 return gEmptyString;
253 }
254 // Convert to UTF-16 here instead of calling NewStringUTF. NewStringUTF
255 // doesn't like UTF-8 strings with high codepoints. It actually expects
256 // Modified UTF-8 with encoded surrogate pairs.
Jeff Brown0cde89f2011-10-10 14:50:10 -0700257 String16 utf16(value, sizeIncludingNull - 1);
Jeff Brown715311f2011-10-07 14:17:09 -0700258 return env->NewString(reinterpret_cast<const jchar*>(utf16.string()), utf16.size());
Jeff Brown0cde89f2011-10-10 14:50:10 -0700259 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700260 int64_t value = window->getFieldSlotValueLong(fieldSlot);
261 char buf[32];
Mark Salyzyn85394032014-04-16 10:28:37 -0700262 snprintf(buf, sizeof(buf), "%" PRId64, value);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700263 return env->NewStringUTF(buf);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700264 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700265 double value = window->getFieldSlotValueDouble(fieldSlot);
266 char buf[32];
267 snprintf(buf, sizeof(buf), "%g", value);
268 return env->NewStringUTF(buf);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700269 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700270 return NULL;
Jeff Brown0cde89f2011-10-10 14:50:10 -0700271 } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700272 throw_sqlite3_exception(env, "Unable to convert BLOB to string");
273 return NULL;
274 } else {
275 throwUnknownTypeException(env, type);
276 return NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 }
278}
279
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700280static jcharArray allocCharArrayBuffer(JNIEnv* env, jobject bufferObj, size_t size) {
281 jcharArray dataObj = jcharArray(env->GetObjectField(bufferObj,
282 gCharArrayBufferClassInfo.data));
283 if (dataObj && size) {
284 jsize capacity = env->GetArrayLength(dataObj);
285 if (size_t(capacity) < size) {
286 env->DeleteLocalRef(dataObj);
287 dataObj = NULL;
288 }
289 }
290 if (!dataObj) {
291 jsize capacity = size;
292 if (capacity < 64) {
293 capacity = 64;
294 }
295 dataObj = env->NewCharArray(capacity); // might throw OOM
296 if (dataObj) {
297 env->SetObjectField(bufferObj, gCharArrayBufferClassInfo.data, dataObj);
298 }
299 }
300 return dataObj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301}
302
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700303static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj,
304 const char* str, size_t len) {
305 ssize_t size = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str), len);
306 if (size < 0) {
307 size = 0; // invalid UTF8 string
308 }
309 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, size);
310 if (dataObj) {
311 if (size) {
312 jchar* data = static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, NULL));
Jeff Brownd0ff68d2011-10-07 13:28:18 -0700313 utf8_to_utf16_no_null_terminator(reinterpret_cast<const uint8_t*>(str), len,
Sergio Giro8f7b8a12016-07-21 14:44:07 +0100314 reinterpret_cast<char16_t*>(data), (size_t) size);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700315 env->ReleasePrimitiveArrayCritical(dataObj, data, 0);
316 }
317 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, size);
318 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319}
320
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700321static void clearCharArrayBuffer(JNIEnv* env, jobject bufferObj) {
322 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, 0);
323 if (dataObj) {
324 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, 0);
325 }
326}
327
Ashok Bhat738702d2014-01-02 13:42:56 +0000328static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700329 jint row, jint column, jobject bufferObj) {
330 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
331 LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
332
Jeff Brown0cde89f2011-10-10 14:50:10 -0700333 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700334 if (!fieldSlot) {
335 throwExceptionWithRowCol(env, row, column);
336 return;
337 }
338
Jeff Brown0cde89f2011-10-10 14:50:10 -0700339 int32_t type = window->getFieldSlotType(fieldSlot);
340 if (type == CursorWindow::FIELD_TYPE_STRING) {
341 size_t sizeIncludingNull;
342 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
343 if (sizeIncludingNull > 1) {
344 fillCharArrayBufferUTF(env, bufferObj, value, sizeIncludingNull - 1);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700345 } else {
346 clearCharArrayBuffer(env, bufferObj);
347 }
Jeff Brown0cde89f2011-10-10 14:50:10 -0700348 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700349 int64_t value = window->getFieldSlotValueLong(fieldSlot);
350 char buf[32];
Mark Salyzyn85394032014-04-16 10:28:37 -0700351 snprintf(buf, sizeof(buf), "%" PRId64, value);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700352 fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf));
Jeff Brown0cde89f2011-10-10 14:50:10 -0700353 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700354 double value = window->getFieldSlotValueDouble(fieldSlot);
355 char buf[32];
356 snprintf(buf, sizeof(buf), "%g", value);
357 fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf));
Jeff Brown0cde89f2011-10-10 14:50:10 -0700358 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700359 clearCharArrayBuffer(env, bufferObj);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700360 } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700361 throw_sqlite3_exception(env, "Unable to convert BLOB to string");
362 } else {
363 throwUnknownTypeException(env, type);
364 }
365}
366
Ashok Bhat738702d2014-01-02 13:42:56 +0000367static jlong nativeGetLong(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700368 jint row, jint column) {
369 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
370 LOG_WINDOW("Getting long for %d,%d from %p", row, column, window);
371
Jeff Brown0cde89f2011-10-10 14:50:10 -0700372 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700373 if (!fieldSlot) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 throwExceptionWithRowCol(env, row, column);
375 return 0;
376 }
377
Jeff Brown0cde89f2011-10-10 14:50:10 -0700378 int32_t type = window->getFieldSlotType(fieldSlot);
379 if (type == CursorWindow::FIELD_TYPE_INTEGER) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700380 return window->getFieldSlotValueLong(fieldSlot);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700381 } else if (type == CursorWindow::FIELD_TYPE_STRING) {
382 size_t sizeIncludingNull;
383 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
384 return sizeIncludingNull > 1 ? strtoll(value, NULL, 0) : 0L;
385 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700386 return jlong(window->getFieldSlotValueDouble(fieldSlot));
Jeff Brown0cde89f2011-10-10 14:50:10 -0700387 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 return 0;
Jeff Brown0cde89f2011-10-10 14:50:10 -0700389 } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 throw_sqlite3_exception(env, "Unable to convert BLOB to long");
391 return 0;
392 } else {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700393 throwUnknownTypeException(env, type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 return 0;
395 }
396}
397
Ashok Bhat738702d2014-01-02 13:42:56 +0000398static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700399 jint row, jint column) {
400 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
401 LOG_WINDOW("Getting double for %d,%d from %p", row, column, window);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402
Jeff Brown0cde89f2011-10-10 14:50:10 -0700403 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700404 if (!fieldSlot) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 throwExceptionWithRowCol(env, row, column);
406 return 0.0;
407 }
408
Jeff Brown0cde89f2011-10-10 14:50:10 -0700409 int32_t type = window->getFieldSlotType(fieldSlot);
410 if (type == CursorWindow::FIELD_TYPE_FLOAT) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700411 return window->getFieldSlotValueDouble(fieldSlot);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700412 } else if (type == CursorWindow::FIELD_TYPE_STRING) {
413 size_t sizeIncludingNull;
414 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
415 return sizeIncludingNull > 1 ? strtod(value, NULL) : 0.0;
416 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700417 return jdouble(window->getFieldSlotValueLong(fieldSlot));
Jeff Brown0cde89f2011-10-10 14:50:10 -0700418 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 return 0.0;
Jeff Brown0cde89f2011-10-10 14:50:10 -0700420 } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 throw_sqlite3_exception(env, "Unable to convert BLOB to double");
422 return 0.0;
423 } else {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700424 throwUnknownTypeException(env, type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 return 0.0;
426 }
427}
428
Ashok Bhat738702d2014-01-02 13:42:56 +0000429static jboolean nativePutBlob(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700430 jbyteArray valueObj, jint row, jint column) {
431 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700432 jsize len = env->GetArrayLength(valueObj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700434 void* value = env->GetPrimitiveArrayCritical(valueObj, NULL);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700435 status_t status = window->putBlob(row, column, value, len);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700436 env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT);
437
Jeff Brown0cde89f2011-10-10 14:50:10 -0700438 if (status) {
439 LOG_WINDOW("Failed to put blob. error=%d", status);
440 return false;
441 }
442
443 LOG_WINDOW("%d,%d is BLOB with %u bytes", row, column, len);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 return true;
445}
446
Ashok Bhat738702d2014-01-02 13:42:56 +0000447static jboolean nativePutString(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700448 jstring valueObj, jint row, jint column) {
449 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450
Jeff Brown0cde89f2011-10-10 14:50:10 -0700451 size_t sizeIncludingNull = env->GetStringUTFLength(valueObj) + 1;
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700452 const char* valueStr = env->GetStringUTFChars(valueObj, NULL);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700453 if (!valueStr) {
Jeff Brown0cde89f2011-10-10 14:50:10 -0700454 LOG_WINDOW("value can't be transferred to UTFChars");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 return false;
456 }
Jeff Brown0cde89f2011-10-10 14:50:10 -0700457 status_t status = window->putString(row, column, valueStr, sizeIncludingNull);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700458 env->ReleaseStringUTFChars(valueObj, valueStr);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700459
Jeff Brown0cde89f2011-10-10 14:50:10 -0700460 if (status) {
461 LOG_WINDOW("Failed to put string. error=%d", status);
462 return false;
463 }
464
465 LOG_WINDOW("%d,%d is TEXT with %u bytes", row, column, sizeIncludingNull);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 return true;
467}
468
Ashok Bhat738702d2014-01-02 13:42:56 +0000469static jboolean nativePutLong(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700470 jlong value, jint row, jint column) {
471 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700472 status_t status = window->putLong(row, column, value);
473
474 if (status) {
475 LOG_WINDOW("Failed to put long. error=%d", status);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 return false;
477 }
478
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700479 LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, column, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 return true;
481}
482
Ashok Bhat738702d2014-01-02 13:42:56 +0000483static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700484 jdouble value, jint row, jint column) {
485 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700486 status_t status = window->putDouble(row, column, value);
487
488 if (status) {
489 LOG_WINDOW("Failed to put double. error=%d", status);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 return false;
491 }
492
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700493 LOG_WINDOW("%d,%d is FLOAT %lf", row, column, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 return true;
495}
496
Ashok Bhat738702d2014-01-02 13:42:56 +0000497static jboolean nativePutNull(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700498 jint row, jint column) {
499 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700500 status_t status = window->putNull(row, column);
501
502 if (status) {
503 LOG_WINDOW("Failed to put null. error=%d", status);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 return false;
505 }
506
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700507 LOG_WINDOW("%d,%d is NULL", row, column);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 return true;
509}
510
Daniel Micay76f6a862015-09-19 17:31:01 -0400511static const JNINativeMethod sMethods[] =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512{
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700513 /* name, signature, funcPtr */
Ashok Bhat738702d2014-01-02 13:42:56 +0000514 { "nativeCreate", "(Ljava/lang/String;I)J",
Jeff Brown0cde89f2011-10-10 14:50:10 -0700515 (void*)nativeCreate },
Ashok Bhat738702d2014-01-02 13:42:56 +0000516 { "nativeCreateFromParcel", "(Landroid/os/Parcel;)J",
Jeff Brown0cde89f2011-10-10 14:50:10 -0700517 (void*)nativeCreateFromParcel },
Ashok Bhat738702d2014-01-02 13:42:56 +0000518 { "nativeDispose", "(J)V",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700519 (void*)nativeDispose },
Ashok Bhat738702d2014-01-02 13:42:56 +0000520 { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
Jeff Brown0cde89f2011-10-10 14:50:10 -0700521 (void*)nativeWriteToParcel },
Chris Craik0834e352017-06-16 10:34:11 -0700522
Ashok Bhat738702d2014-01-02 13:42:56 +0000523 { "nativeGetName", "(J)Ljava/lang/String;",
Jeff Brown650de3d2011-10-27 14:52:28 -0700524 (void*)nativeGetName },
Chris Craik0834e352017-06-16 10:34:11 -0700525 { "nativeGetBlob", "(JII)[B",
526 (void*)nativeGetBlob },
527 { "nativeGetString", "(JII)Ljava/lang/String;",
528 (void*)nativeGetString },
529 { "nativeCopyStringToBuffer", "(JIILandroid/database/CharArrayBuffer;)V",
530 (void*)nativeCopyStringToBuffer },
531 { "nativePutBlob", "(J[BII)Z",
532 (void*)nativePutBlob },
533 { "nativePutString", "(JLjava/lang/String;II)Z",
534 (void*)nativePutString },
535
536 // ------- @FastNative below here ----------------------
Ashok Bhat738702d2014-01-02 13:42:56 +0000537 { "nativeClear", "(J)V",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700538 (void*)nativeClear },
Ashok Bhat738702d2014-01-02 13:42:56 +0000539 { "nativeGetNumRows", "(J)I",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700540 (void*)nativeGetNumRows },
Ashok Bhat738702d2014-01-02 13:42:56 +0000541 { "nativeSetNumColumns", "(JI)Z",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700542 (void*)nativeSetNumColumns },
Ashok Bhat738702d2014-01-02 13:42:56 +0000543 { "nativeAllocRow", "(J)Z",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700544 (void*)nativeAllocRow },
Ashok Bhat738702d2014-01-02 13:42:56 +0000545 { "nativeFreeLastRow", "(J)V",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700546 (void*)nativeFreeLastRow },
Ashok Bhat738702d2014-01-02 13:42:56 +0000547 { "nativeGetType", "(JII)I",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700548 (void*)nativeGetType },
Ashok Bhat738702d2014-01-02 13:42:56 +0000549 { "nativeGetLong", "(JII)J",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700550 (void*)nativeGetLong },
Ashok Bhat738702d2014-01-02 13:42:56 +0000551 { "nativeGetDouble", "(JII)D",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700552 (void*)nativeGetDouble },
Chris Craik0834e352017-06-16 10:34:11 -0700553
Ashok Bhat738702d2014-01-02 13:42:56 +0000554 { "nativePutLong", "(JJII)Z",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700555 (void*)nativePutLong },
Ashok Bhat738702d2014-01-02 13:42:56 +0000556 { "nativePutDouble", "(JDII)Z",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700557 (void*)nativePutDouble },
Ashok Bhat738702d2014-01-02 13:42:56 +0000558 { "nativePutNull", "(JII)Z",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700559 (void*)nativePutNull },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560};
561
Andreas Gampe987f79f2014-11-18 17:29:46 -0800562int register_android_database_CursorWindow(JNIEnv* env)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563{
Andreas Gampe987f79f2014-11-18 17:29:46 -0800564 jclass clazz = FindClassOrDie(env, "android/database/CharArrayBuffer");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565
Andreas Gampe987f79f2014-11-18 17:29:46 -0800566 gCharArrayBufferClassInfo.data = GetFieldIDOrDie(env, clazz, "data", "[C");
567 gCharArrayBufferClassInfo.sizeCopied = GetFieldIDOrDie(env, clazz, "sizeCopied", "I");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568
Andreas Gampe987f79f2014-11-18 17:29:46 -0800569 gEmptyString = MakeGlobalRefOrDie(env, env->NewStringUTF(""));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570
Andreas Gampe987f79f2014-11-18 17:29:46 -0800571 return RegisterMethodsOrDie(env, "android/database/CursorWindow", sMethods, NELEM(sMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572}
573
574} // namespace android