Issue #1053 - Remove android support from mozglue

Yes, I checked for unsaved files this time...
pull/24/head
Matt A. Tobin 3 years ago committed by Roy Tam
parent bb8b180863
commit a890ecd06f
  1. 465
      mozglue/android/APKOpen.cpp
  2. 39
      mozglue/android/APKOpen.h
  3. 285
      mozglue/android/NSSBridge.cpp
  4. 42
      mozglue/android/NSSBridge.h
  5. 132
      mozglue/android/NativeCrypto.cpp
  6. 53
      mozglue/android/NativeCrypto.h
  7. 413
      mozglue/android/SQLiteBridge.cpp
  8. 34
      mozglue/android/SQLiteBridge.h
  9. 65
      mozglue/android/SharedMemNatives.cpp
  10. 58
      mozglue/android/moz.build
  11. 124
      mozglue/android/nsGeckoUtils.cpp
  12. 432
      mozglue/android/pbkdf2_sha256.c
  13. 70
      mozglue/android/pbkdf2_sha256.h
  14. 140
      mozglue/build/BionicGlue.cpp
  15. 4
      mozglue/build/arm.cpp
  16. 2
      mozglue/build/arm.h
  17. 53
      mozglue/build/moz.build
  18. 10
      mozglue/linker/CustomElf.cpp
  19. 164
      mozglue/linker/ElfLoader.cpp
  20. 8
      mozglue/linker/ElfLoader.h
  21. 8
      mozglue/linker/Elfxx.h
  22. 35
      mozglue/linker/Logging.h
  23. 95
      mozglue/linker/Mappable.cpp
  24. 31
      mozglue/misc/StackWalk.cpp
  25. 2
      mozglue/misc/TimeStamp_posix.cpp
  26. 3
      mozglue/moz.build

@ -1,465 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* This custom library loading code is only meant to be called
* during initialization. As a result, it takes no special
* precautions to be threadsafe. Any of the library loading functions
* like mozload should not be available to other code.
*/
#include <jni.h>
#include <android/log.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/limits.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <zlib.h>
#include "dlfcn.h"
#include "APKOpen.h"
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include "sqlite3.h"
#include "SQLiteBridge.h"
#include "NSSBridge.h"
#include "ElfLoader.h"
#include "application.ini.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "XREChildData.h"
/* Android headers don't define RUSAGE_THREAD */
#ifndef RUSAGE_THREAD
#define RUSAGE_THREAD 1
#endif
#ifndef RELEASE_OR_BETA
/* Official builds have the debuggable flag set to false, which disables
* the backtrace dumper from bionic. However, as it is useful for native
* crashes happening before the crash reporter is registered, re-enable
* it on non release builds (i.e. nightly and aurora).
* Using a constructor so that it is re-enabled as soon as libmozglue.so
* is loaded.
*/
__attribute__((constructor))
void make_dumpable() {
prctl(PR_SET_DUMPABLE, 1);
}
#endif
extern "C" {
/*
* To work around http://code.google.com/p/android/issues/detail?id=23203
* we don't link with the crt objects. In some configurations, this means
* a lack of the __dso_handle symbol because it is defined there, and
* depending on the android platform and ndk versions used, it may or may
* not be defined in libc.so. In the latter case, we fail to link. Defining
* it here as weak makes us provide the symbol when it's not provided by
* the crt objects, making the change transparent for future NDKs that
* would fix the original problem. On older NDKs, it is not a problem
* either because the way __dso_handle was used was already broken (and
* the custom linker works around it).
*/
NS_EXPORT __attribute__((weak)) void *__dso_handle;
}
typedef int mozglueresult;
enum StartupEvent {
#define mozilla_StartupTimeline_Event(ev, z) ev,
#include "StartupTimeline.h"
#undef mozilla_StartupTimeline_Event
MAX_STARTUP_EVENT_ID
};
using namespace mozilla;
static const int MAX_MAPPING_INFO = 32;
static mapping_info lib_mapping[MAX_MAPPING_INFO];
NS_EXPORT const struct mapping_info *
getLibraryMapping()
{
return lib_mapping;
}
void
JNI_Throw(JNIEnv* jenv, const char* classname, const char* msg)
{
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Throw\n");
jclass cls = jenv->FindClass(classname);
if (cls == nullptr) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't find exception class (or exception pending) %s\n", classname);
exit(FAILURE);
}
int rc = jenv->ThrowNew(cls, msg);
if (rc < 0) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Error throwing exception %s\n", msg);
exit(FAILURE);
}
jenv->DeleteLocalRef(cls);
}
namespace {
JavaVM* sJavaVM;
pthread_t sJavaUiThread;
}
void
abortThroughJava(const char* msg)
{
struct sigaction sigact = {};
if (SEGVHandler::__wrap_sigaction(SIGSEGV, nullptr, &sigact)) {
return; // sigaction call failed.
}
Dl_info info = {};
if ((sigact.sa_flags & SA_SIGINFO) &&
__wrap_dladdr(reinterpret_cast<void*>(sigact.sa_sigaction), &info) &&
info.dli_fname && strstr(info.dli_fname, "libxul.so")) {
return; // Existing signal handler is in libxul (i.e. we have crash reporter).
}
JNIEnv* env = nullptr;
if (!sJavaVM || sJavaVM->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
return;
}
if (!env || env->PushLocalFrame(2) != JNI_OK) {
return;
}
jclass loader = env->FindClass("org/mozilla/gecko/mozglue/GeckoLoader");
if (!loader) {
return;
}
jmethodID method = env->GetStaticMethodID(loader, "abort", "(Ljava/lang/String;)V");
jstring str = env->NewStringUTF(msg);
if (method && str) {
env->CallStaticVoidMethod(loader, method, str);
}
env->PopLocalFrame(nullptr);
}
NS_EXPORT pthread_t
getJavaUiThread()
{
return sJavaUiThread;
}
extern "C" NS_EXPORT void MOZ_JNICALL
Java_org_mozilla_gecko_GeckoThread_registerUiThread(JNIEnv*, jclass)
{
sJavaUiThread = pthread_self();
}
static void * xul_handle = nullptr;
#ifndef MOZ_FOLD_LIBS
static void * sqlite_handle = nullptr;
static void * nspr_handle = nullptr;
static void * plc_handle = nullptr;
#else
#define sqlite_handle nss_handle
#define nspr_handle nss_handle
#define plc_handle nss_handle
#endif
static void * nss_handle = nullptr;
template <typename T> inline void
xul_dlsym(const char *symbolName, T *value)
{
*value = (T) (uintptr_t) __wrap_dlsym(xul_handle, symbolName);
}
static int mapping_count = 0;
extern "C" void
report_mapping(char *name, void *base, uint32_t len, uint32_t offset)
{
if (mapping_count >= MAX_MAPPING_INFO)
return;
struct mapping_info *info = &lib_mapping[mapping_count++];
info->name = strdup(name);
info->base = (uintptr_t)base;
info->len = len;
info->offset = offset;
}
extern "C" void
delete_mapping(const char *name)
{
for (int pos = 0; pos < mapping_count; ++pos) {
struct mapping_info *info = &lib_mapping[pos];
if (!strcmp(info->name, name)) {
struct mapping_info *last = &lib_mapping[mapping_count - 1];
free(info->name);
*info = *last;
--mapping_count;
break;
}
}
}
static void*
dlopenAPKLibrary(const char* apkName, const char* libraryName)
{
#define APK_ASSETS_PATH "!/assets/" ANDROID_CPU_ARCH "/"
size_t filenameLength = strlen(apkName) +
sizeof(APK_ASSETS_PATH) + // includes \0 terminator
strlen(libraryName);
auto file = MakeUnique<char[]>(filenameLength);
snprintf(file.get(), filenameLength, "%s" APK_ASSETS_PATH "%s",
apkName, libraryName);
return __wrap_dlopen(file.get(), RTLD_GLOBAL | RTLD_LAZY);
#undef APK_ASSETS_PATH
}
static mozglueresult
loadGeckoLibs(const char *apkName)
{
TimeStamp t0 = TimeStamp::Now();
struct rusage usage1_thread, usage1;
getrusage(RUSAGE_THREAD, &usage1_thread);
getrusage(RUSAGE_SELF, &usage1);
xul_handle = dlopenAPKLibrary(apkName, "libxul.so");
if (!xul_handle) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libxul!");
return FAILURE;
}
void (*XRE_StartupTimelineRecord)(int, TimeStamp);
xul_dlsym("XRE_StartupTimelineRecord", &XRE_StartupTimelineRecord);
TimeStamp t1 = TimeStamp::Now();
struct rusage usage2_thread, usage2;
getrusage(RUSAGE_THREAD, &usage2_thread);
getrusage(RUSAGE_SELF, &usage2);
#define RUSAGE_TIMEDIFF(u1, u2, field) \
((u2.ru_ ## field.tv_sec - u1.ru_ ## field.tv_sec) * 1000 + \
(u2.ru_ ## field.tv_usec - u1.ru_ ## field.tv_usec) / 1000)
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loaded libs in %fms total, %ldms(%ldms) user, %ldms(%ldms) system, %ld(%ld) faults",
(t1 - t0).ToMilliseconds(),
RUSAGE_TIMEDIFF(usage1_thread, usage2_thread, utime),
RUSAGE_TIMEDIFF(usage1, usage2, utime),
RUSAGE_TIMEDIFF(usage1_thread, usage2_thread, stime),
RUSAGE_TIMEDIFF(usage1, usage2, stime),
usage2_thread.ru_majflt - usage1_thread.ru_majflt,
usage2.ru_majflt - usage1.ru_majflt);
XRE_StartupTimelineRecord(LINKER_INITIALIZED, t0);
XRE_StartupTimelineRecord(LIBRARIES_LOADED, t1);
return SUCCESS;
}
static mozglueresult loadNSSLibs(const char *apkName);
static mozglueresult
loadSQLiteLibs(const char *apkName)
{
if (sqlite_handle)
return SUCCESS;
#ifdef MOZ_FOLD_LIBS
if (loadNSSLibs(apkName) != SUCCESS)
return FAILURE;
#else
sqlite_handle = dlopenAPKLibrary(apkName, "libmozsqlite3.so");
if (!sqlite_handle) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libmozsqlite3!");
return FAILURE;
}
#endif
setup_sqlite_functions(sqlite_handle);
return SUCCESS;
}
static mozglueresult
loadNSSLibs(const char *apkName)
{
if (nss_handle && nspr_handle && plc_handle)
return SUCCESS;
nss_handle = dlopenAPKLibrary(apkName, "libnss3.so");
#ifndef MOZ_FOLD_LIBS
nspr_handle = dlopenAPKLibrary(apkName, "libnspr4.so");
plc_handle = dlopenAPKLibrary(apkName, "libplc4.so");
#endif
if (!nss_handle) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnss3!");
return FAILURE;
}
#ifndef MOZ_FOLD_LIBS
if (!nspr_handle) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnspr4!");
return FAILURE;
}
if (!plc_handle) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libplc4!");
return FAILURE;
}
#endif
return setup_nss_functions(nss_handle, nspr_handle, plc_handle);
}
extern "C" NS_EXPORT void MOZ_JNICALL
Java_org_mozilla_gecko_mozglue_GeckoLoader_extractGeckoLibsNative(
JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName)
{
MOZ_ALWAYS_TRUE(!jenv->GetJavaVM(&sJavaVM));
const char* apkName = jenv->GetStringUTFChars(jApkName, nullptr);
if (apkName == nullptr) {
return;
}
// Extract and cache native lib to allow for efficient startup from cache.
void* handle = dlopenAPKLibrary(apkName, "libxul.so");
if (handle) {
__android_log_print(ANDROID_LOG_INFO, "GeckoLibLoad",
"Extracted and cached libxul.so.");
// We have extracted and cached the lib, we can close it now.
__wrap_dlclose(handle);
} else {
JNI_Throw(jenv, "java/lang/Exception", "Error extracting gecko libraries");
}
jenv->ReleaseStringUTFChars(jApkName, apkName);
}
extern "C" NS_EXPORT void MOZ_JNICALL
Java_org_mozilla_gecko_mozglue_GeckoLoader_loadGeckoLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName)
{
jenv->GetJavaVM(&sJavaVM);
const char* str;
// XXX: java doesn't give us true UTF8, we should figure out something
// better to do here
str = jenv->GetStringUTFChars(jApkName, nullptr);
if (str == nullptr)
return;
int res = loadGeckoLibs(str);
if (res != SUCCESS) {
JNI_Throw(jenv, "java/lang/Exception", "Error loading gecko libraries");
}
jenv->ReleaseStringUTFChars(jApkName, str);
}
extern "C" NS_EXPORT void MOZ_JNICALL
Java_org_mozilla_gecko_mozglue_GeckoLoader_loadSQLiteLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) {
const char* str;
// XXX: java doesn't give us true UTF8, we should figure out something
// better to do here
str = jenv->GetStringUTFChars(jApkName, nullptr);
if (str == nullptr)
return;
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite start\n");
mozglueresult rv = loadSQLiteLibs(str);
if (rv != SUCCESS) {
JNI_Throw(jenv, "java/lang/Exception", "Error loading sqlite libraries");
}
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite done\n");
jenv->ReleaseStringUTFChars(jApkName, str);
}
extern "C" NS_EXPORT void MOZ_JNICALL
Java_org_mozilla_gecko_mozglue_GeckoLoader_loadNSSLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) {
const char* str;
// XXX: java doesn't give us true UTF8, we should figure out something
// better to do here
str = jenv->GetStringUTFChars(jApkName, nullptr);
if (str == nullptr)
return;
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss start\n");
mozglueresult rv = loadNSSLibs(str);
if (rv != SUCCESS) {
JNI_Throw(jenv, "java/lang/Exception", "Error loading nss libraries");
}
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss done\n");
jenv->ReleaseStringUTFChars(jApkName, str);
}
typedef void (*GeckoStart_t)(JNIEnv*, char*, const nsXREAppData*);
extern "C" NS_EXPORT void MOZ_JNICALL
Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv *jenv, jclass jc, jstring jargs)
{
GeckoStart_t GeckoStart;
xul_dlsym("GeckoStart", &GeckoStart);
if (GeckoStart == nullptr)
return;
// XXX: java doesn't give us true UTF8, we should figure out something
// better to do here
int len = jenv->GetStringUTFLength(jargs);
// GeckoStart needs to write in the args buffer, so we need a copy.
char *args = (char *) malloc(len + 1);
jenv->GetStringUTFRegion(jargs, 0, len, args);
args[len] = '\0';
ElfLoader::Singleton.ExpectShutdown(false);
GeckoStart(jenv, args, &sAppData);
ElfLoader::Singleton.ExpectShutdown(true);
free(args);
}
typedef int GeckoProcessType;
extern "C" NS_EXPORT mozglueresult
ChildProcessInit(int argc, char* argv[])
{
int i;
for (i = 0; i < (argc - 1); i++) {
if (strcmp(argv[i], "-greomni"))
continue;
i = i + 1;
break;
}
if (loadNSSLibs(argv[i]) != SUCCESS) {
return FAILURE;
}
if (loadSQLiteLibs(argv[i]) != SUCCESS) {
return FAILURE;
}
if (loadGeckoLibs(argv[i]) != SUCCESS) {
return FAILURE;
}
void (*fXRE_SetProcessType)(char*);
xul_dlsym("XRE_SetProcessType", &fXRE_SetProcessType);
mozglueresult (*fXRE_InitChildProcess)(int, char**, void*);
xul_dlsym("XRE_InitChildProcess", &fXRE_InitChildProcess);
fXRE_SetProcessType(argv[--argc]);
XREChildData childData;
return fXRE_InitChildProcess(argc, argv, &childData);
}

@ -1,39 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef APKOpen_h
#define APKOpen_h
#include <jni.h>
#include <pthread.h>
#ifndef NS_EXPORT
#define NS_EXPORT __attribute__ ((visibility("default")))
#endif
struct mapping_info {
char * name;
uintptr_t base;
size_t len;
size_t offset;
};
NS_EXPORT const struct mapping_info * getLibraryMapping();
NS_EXPORT void abortThroughJava(const char* msg);
NS_EXPORT pthread_t getJavaUiThread();
static const int SUCCESS = 0;
static const int FAILURE = 1;
void JNI_Throw(JNIEnv* jenv, const char* classname, const char* msg);
// Bug 1207642 - Work around Dalvik bug by realigning stack on JNI entry
#ifndef MOZ_JNICALL
# ifdef __i386__
# define MOZ_JNICALL JNICALL __attribute__((force_align_arg_pointer))
# else
# define MOZ_JNICALL JNICALL
# endif
#endif
#endif /* APKOpen_h */

@ -1,285 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <stdlib.h>
#include "dlfcn.h"
#include "NSSBridge.h"
#include "APKOpen.h"
#ifdef ANDROID
#include <jni.h>
#include <android/log.h>
#endif
#include "ElfLoader.h"
#ifdef DEBUG
#define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x)
#else
#define LOG(x...)
#endif
static bool initialized = false;
#define NSS_WRAPPER_INT(name) name ## _t f_ ## name;
NSS_WRAPPER_INT(NSS_Initialize)
NSS_WRAPPER_INT(NSS_Shutdown)
NSS_WRAPPER_INT(SECITEM_ZfreeItem)
NSS_WRAPPER_INT(PK11SDR_Encrypt)
NSS_WRAPPER_INT(PK11SDR_Decrypt)
NSS_WRAPPER_INT(PK11_GetInternalKeySlot)
NSS_WRAPPER_INT(PK11_NeedUserInit)
NSS_WRAPPER_INT(PK11_InitPin)
NSS_WRAPPER_INT(PR_ErrorToString)
NSS_WRAPPER_INT(PR_GetError)
NSS_WRAPPER_INT(PR_Free)
NSS_WRAPPER_INT(PL_Base64Encode)
NSS_WRAPPER_INT(PL_Base64Decode)
NSS_WRAPPER_INT(PL_strfree)
SECStatus doCrypto(JNIEnv* jenv, const char *path, const char *value, char** result, bool doEncrypt);
SECStatus encode(const uint8_t* data, uint32_t srclen, char** result);
SECStatus decode(const char* data, uint8_t** result, uint32_t* length);
int
setup_nss_functions(void *nss_handle,
void *nspr_handle,
void *plc_handle)
{
if (nss_handle == nullptr || nspr_handle == nullptr || plc_handle == nullptr) {
LOG("Missing handle\n");
return FAILURE;
}
#define GETFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(nss_handle, #name); \
if (!f_ ##name) { __android_log_print(ANDROID_LOG_ERROR, "GeckoJNI", "missing %s", #name); return FAILURE; }
GETFUNC(NSS_Initialize);
GETFUNC(NSS_Shutdown);
GETFUNC(PK11SDR_Encrypt);
GETFUNC(PK11SDR_Decrypt);
GETFUNC(PK11_GetInternalKeySlot);
GETFUNC(PK11_NeedUserInit);
GETFUNC(PK11_InitPin);
GETFUNC(SECITEM_ZfreeItem);
#undef GETFUNC
#define NSPRFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(nspr_handle, #name); \
if (!f_ ##name) { __android_log_print(ANDROID_LOG_ERROR, "GeckoJNI", "missing %s", #name); return FAILURE; }
NSPRFUNC(PR_ErrorToString);
NSPRFUNC(PR_GetError);
NSPRFUNC(PR_Free);
#undef NSPRFUNC
#define PLCFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(plc_handle, #name); \
if (!f_ ##name) { __android_log_print(ANDROID_LOG_ERROR, "GeckoJNI", "missing %s", #name); return FAILURE; }
PLCFUNC(PL_Base64Encode);
PLCFUNC(PL_Base64Decode);
PLCFUNC(PL_strfree);
#undef PLCFUNC
return SUCCESS;
}
/* Throws the current NSS error. */
static void
throwError(JNIEnv* jenv, const char * funcString) {
char *msg;
PRErrorCode perr = f_PR_GetError();
char * errString = f_PR_ErrorToString(perr, 0);
asprintf(&msg, "%s returned error %d: %s\n", funcString, perr, errString);
LOG("Throwing error: %s\n", msg);
JNI_Throw(jenv, "java/lang/Exception", msg);
free(msg);
LOG("Error thrown\n");
}
extern "C" NS_EXPORT jstring MOZ_JNICALL
Java_org_mozilla_gecko_NSSBridge_nativeEncrypt(JNIEnv* jenv, jclass,
jstring jPath,
jstring jValue)
{
jstring ret = jenv->NewStringUTF("");
const char* path;
path = jenv->GetStringUTFChars(jPath, nullptr);
const char* value;
value = jenv->GetStringUTFChars(jValue, nullptr);
char* result;
SECStatus rv = doCrypto(jenv, path, value, &result, true);
if (rv == SECSuccess) {
ret = jenv->NewStringUTF(result);
free(result);
}
jenv->ReleaseStringUTFChars(jValue, value);
jenv->ReleaseStringUTFChars(jPath, path);
return ret;
}
extern "C" NS_EXPORT jstring MOZ_JNICALL
Java_org_mozilla_gecko_NSSBridge_nativeDecrypt(JNIEnv* jenv, jclass,
jstring jPath,
jstring jValue)
{
jstring ret = jenv->NewStringUTF("");
const char* path;
path = jenv->GetStringUTFChars(jPath, nullptr);
const char* value;
value = jenv->GetStringUTFChars(jValue, nullptr);
char* result;
SECStatus rv = doCrypto(jenv, path, value, &result, false);
if (rv == SECSuccess) {
ret = jenv->NewStringUTF(result);
free(result);
}
jenv->ReleaseStringUTFChars(jValue, value);
jenv->ReleaseStringUTFChars(jPath, path);
return ret;
}
/* Encrypts or decrypts a string. result should be freed with free() when done */
SECStatus
doCrypto(JNIEnv* jenv, const char *path, const char *value, char** result, bool encrypt)
{
SECStatus rv;
PK11SlotInfo *slot;
if (!initialized) {
LOG("Initialize crypto in %s\n", path);
rv = f_NSS_Initialize(path, "", "", "secmod.db", NSS_INIT_NOROOTINIT);
if (rv != SECSuccess) {
throwError(jenv, "NSS_Initialize");
return rv;
}
initialized = true;
}
slot = f_PK11_GetInternalKeySlot();
if (!slot) {
throwError(jenv, "PK11_GetInternalKeySlot");
return SECFailure;
}
if (f_PK11_NeedUserInit(slot)) {
LOG("Initializing key3.db with default blank password.\n");
rv = f_PK11_InitPin(slot, nullptr, nullptr);
if (rv != SECSuccess) {
throwError(jenv, "PK11_InitPin");
return rv;
}
}
SECItem request;
SECItem reply;
reply.data = 0;
reply.len = 0;
if (encrypt) {
// This can print sensitive data. Uncomment if you need it.
// LOG("Encrypting: %s\n", value);
request.data = (unsigned char*)value;
request.len = strlen(value);
SECItem keyid;
keyid.data = 0;
keyid.len = 0;
rv = f_PK11SDR_Encrypt(&keyid, &request, &reply, nullptr);
if (rv == SECSuccess) {
rv = encode(reply.data, reply.len, result);
if (rv == SECSuccess) {
LOG("Encrypted: %s\n", *result);
} else {
throwError(jenv, "encode");
}
} else {
throwError(jenv, "PK11SDR_Encrypt");
}
} else {
LOG("Decoding: %s\n", value);
rv = decode(value, &request.data, &request.len);
if (rv != SECSuccess) {
throwError(jenv, "decode");
return rv;
}
rv = f_PK11SDR_Decrypt(&request, &reply, nullptr);
if (rv == SECSuccess) {
*result = static_cast<char*>(malloc(reply.len + 1));
strncpy(*result, reinterpret_cast<char*>(reply.data), reply.len);
(*result)[reply.len] = '\0';
// This can print sensitive data. Uncomment if you need it.
// LOG("Decoded %i letters: %s\n", reply.len, *result);
} else {
throwError(jenv, "PK11SDR_Decrypt");
}
free(request.data);
}
f_SECITEM_ZfreeItem(&reply, false);
return rv;
}
/*
* Base64 encodes the data passed in. The caller must deallocate _retval using free();
*/
SECStatus
encode(const uint8_t* data, uint32_t srclen, char** result)
{
if (srclen > (PR_UINT32_MAX / 4) * 3) {
return SECFailure;
}
const uint32_t dstlen = ((srclen + 2) / 3) * 4;
char* const buffer = static_cast<char*>(malloc(dstlen + 1));
if (!buffer || !f_PL_Base64Encode(reinterpret_cast<const char*>(data), srclen, buffer)) {
free(buffer);
*result = nullptr;
return SECFailure;
}
buffer[dstlen] = '\0';
*result = buffer;
return SECSuccess;
}
/*
* Base64 decodes the data passed in. The caller must deallocate result using free();
*/
SECStatus
decode(const char* data, uint8_t** result, uint32_t* length)
{
uint32_t srclen = strlen(data);
while (srclen && data[srclen - 1] == '=') {
srclen--;
}
// Avoid overflow when calculating result length.
const uint32_t dstlen = (srclen / 4) * 3 + ((srclen % 4) * 3) / 4;
// At most 2 extra bytes due to padding in input.
uint8_t* const buffer = static_cast<uint8_t*>(malloc(dstlen + 2));
if (!buffer || !f_PL_Base64Decode(data, srclen, reinterpret_cast<char*>(buffer))) {
free(buffer);
*result = nullptr;
*length = 0;
return SECFailure;
}
buffer[dstlen] = '\0';
*result = buffer;
*length = dstlen;
return SECSuccess;
}

@ -1,42 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef NSSBridge_h
#define NSSBridge_h
#include "nss.h"
#include "pk11func.h"
#include "pk11sdr.h"
#include "seccomon.h"
#include "secitem.h"
#include "secmodt.h"
#include "prerror.h"
#include "plstr.h"
#include "prmem.h"
#include <jni.h>
int setup_nss_functions(void *nss_handle, void *nssutil_handle, void *plc_handle);
#define NSS_WRAPPER(name, return_type, args...) \
typedef return_type (*name ## _t)(args); \
extern name ## _t f_ ## name;
NSS_WRAPPER(NSS_Initialize, SECStatus, const char*, const char*, const char*, const char*, uint32_t)
NSS_WRAPPER(NSS_Shutdown, void, void)
NSS_WRAPPER(PK11SDR_Encrypt, SECStatus, SECItem *, SECItem *, SECItem *, void *)
NSS_WRAPPER(PK11SDR_Decrypt, SECStatus, SECItem *, SECItem *, void *)
NSS_WRAPPER(SECITEM_ZfreeItem, void, SECItem*, PRBool)
NSS_WRAPPER(PR_ErrorToString, char *, PRErrorCode, PRLanguageCode)
NSS_WRAPPER(PR_GetError, PRErrorCode, void)
NSS_WRAPPER(PR_Free, PRErrorCode, char *)
NSS_WRAPPER(PL_Base64Encode, char*, const char*, uint32_t, char*)
NSS_WRAPPER(PL_Base64Decode, char*, const char*, uint32_t, char*)
NSS_WRAPPER(PL_strfree, void, char*)
NSS_WRAPPER(PK11_GetInternalKeySlot, PK11SlotInfo *, void)
NSS_WRAPPER(PK11_NeedUserInit, PRBool, PK11SlotInfo *)
NSS_WRAPPER(PK11_InitPin, SECStatus, PK11SlotInfo*, const char*, const char*)
#endif /* NSS_h */

@ -1,132 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "NativeCrypto.h"
#include "APKOpen.h"
#include <jni.h>
#include <errno.h>
#include <stdlib.h>
#include <inttypes.h>
#include "mozilla/SHA1.h"
#include "pbkdf2_sha256.h"
/**
* Helper function to invoke native PBKDF2 function with JNI
* arguments.
*/
extern "C" JNIEXPORT jbyteArray MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_pbkdf2SHA256
(JNIEnv *env, jclass jc, jbyteArray jpassword, jbyteArray jsalt, jint c, jint dkLen) {
if (dkLen < 0) {
env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
"dkLen should not be less than 0");
return nullptr;
}
jbyte *password = env->GetByteArrayElements(jpassword, nullptr);
size_t passwordLen = env->GetArrayLength(jpassword);
jbyte *salt = env->GetByteArrayElements(jsalt, nullptr);
size_t saltLen = env->GetArrayLength(jsalt);
uint8_t hashResult[dkLen];
PBKDF2_SHA256((uint8_t *) password, passwordLen, (uint8_t *) salt, saltLen,
(uint64_t) c, hashResult, (size_t) dkLen);
env->ReleaseByteArrayElements(jpassword, password, JNI_ABORT);
env->ReleaseByteArrayElements(jsalt, salt, JNI_ABORT);
jbyteArray out = env->NewByteArray(dkLen);
if (out == nullptr) {
return nullptr;
}
env->SetByteArrayRegion(out, 0, dkLen, (jbyte *) hashResult);
return out;
}
using namespace mozilla;
/**
* Helper function to invoke native SHA-1 function with JNI arguments.
*/
extern "C" JNIEXPORT jbyteArray MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha1
(JNIEnv *env, jclass jc, jbyteArray jstr) {
jbyte *str = env->GetByteArrayElements(jstr, nullptr);
size_t strLen = env->GetArrayLength(jstr);
SHA1Sum sha1;
SHA1Sum::Hash hashResult;
sha1.update((void *) str, (uint32_t) strLen);
sha1.finish(hashResult);
env->ReleaseByteArrayElements(jstr, str, JNI_ABORT);
jbyteArray out = env->NewByteArray(SHA1Sum::kHashSize);
if (out == nullptr) {
return nullptr;
}
env->SetByteArrayRegion(out, 0, SHA1Sum::kHashSize, (jbyte *) hashResult);
return out;
}
/**
* Helper function to invoke native SHA-256 init with JNI arguments.
*/
extern "C" JNIEXPORT jbyteArray MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256init
(JNIEnv *env, jclass jc) {
jbyteArray out = env->NewByteArray(sizeof(SHA256_CTX));
if (nullptr == out) {
return nullptr;
}
SHA256_CTX *shaContext = (SHA256_CTX*)env->GetByteArrayElements(out, nullptr);
SHA256_Init(shaContext);
env->ReleaseByteArrayElements(out, (jbyte*)shaContext, 0);
return out;
}
/**
* Helper function to invoke native SHA-256 update with JNI arguments.
*/
extern "C" JNIEXPORT void MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256update
(JNIEnv *env, jclass jc, jbyteArray jctx, jbyteArray jstr, jint len) {
jbyte *str = env->GetByteArrayElements(jstr, nullptr);
SHA256_CTX *shaContext = (SHA256_CTX*)env->GetByteArrayElements(jctx, nullptr);
SHA256_Update(shaContext, (void*)str, (size_t) len);
env->ReleaseByteArrayElements(jstr, str, JNI_ABORT);
env->ReleaseByteArrayElements(jctx, (jbyte*)shaContext, 0);
return;
}
/**
* Helper function to invoke native SHA-256 finalize with JNI arguments.
*/
extern "C" JNIEXPORT jbyteArray MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256finalize
(JNIEnv *env, jclass jc, jbyteArray jctx) {
SHA256_CTX *shaContext = (SHA256_CTX*)env->GetByteArrayElements(jctx, nullptr);
unsigned char* digest = new unsigned char[32];
SHA256_Final(digest, shaContext);
env->ReleaseByteArrayElements(jctx, (jbyte*)shaContext, JNI_ABORT);
jbyteArray out = env->NewByteArray(32);
if (nullptr != out) {
env->SetByteArrayRegion(out, 0, 32, (jbyte*)digest);
}
delete[] digest;
return out;
}

@ -1,53 +0,0 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_mozilla_gecko_background_nativecode_NativeCrypto */
#ifndef _Included_org_mozilla_gecko_background_nativecode_NativeCrypto
#define _Included_org_mozilla_gecko_background_nativecode_NativeCrypto
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: org_mozilla_gecko_background_nativecode_NativeCrypto
* Method: pbkdf2SHA256
* Signature: ([B[BII)[B
*/
JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_pbkdf2SHA256
(JNIEnv *, jclass, jbyteArray, jbyteArray, jint, jint);
/*
* Class: org_mozilla_gecko_background_nativecode_NativeCrypto
* Method: sha1
* Signature: ([B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha1
(JNIEnv *, jclass, jbyteArray);
/*
* Class: org_mozilla_gecko_background_nativecode_NativeCrypto
* Method: sha256init
* Signature: ()[B
*/
JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256init
(JNIEnv *, jclass);
/*
* Class: org_mozilla_gecko_background_nativecode_NativeCrypto
* Method: sha256update
* Signature: ([B[B)V
*/
JNIEXPORT void JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256update
(JNIEnv *, jclass, jbyteArray, jbyteArray, jint);
/*
* Class: org_mozilla_gecko_background_nativecode_NativeCrypto
* Method: sha256finalize
* Signature: ([B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256finalize
(JNIEnv *, jclass, jbyteArray);
#ifdef __cplusplus
}
#endif
#endif

@ -1,413 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <stdlib.h>
#include <stdio.h>
#include <jni.h>
#include <android/log.h>
#include "dlfcn.h"
#include "APKOpen.h"
#include "ElfLoader.h"
#include "SQLiteBridge.h"
#ifdef DEBUG
#define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x)
#else
#define LOG(x...)
#endif
#define SQLITE_WRAPPER_INT(name) name ## _t f_ ## name;
SQLITE_WRAPPER_INT(sqlite3_open)
SQLITE_WRAPPER_INT(sqlite3_errmsg)
SQLITE_WRAPPER_INT(sqlite3_prepare_v2)
SQLITE_WRAPPER_INT(sqlite3_bind_parameter_count)
SQLITE_WRAPPER_INT(sqlite3_bind_null)
SQLITE_WRAPPER_INT(sqlite3_bind_text)
SQLITE_WRAPPER_INT(sqlite3_step)
SQLITE_WRAPPER_INT(sqlite3_column_count)
SQLITE_WRAPPER_INT(sqlite3_finalize)
SQLITE_WRAPPER_INT(sqlite3_close)
SQLITE_WRAPPER_INT(sqlite3_column_name)
SQLITE_WRAPPER_INT(sqlite3_column_type)
SQLITE_WRAPPER_INT(sqlite3_column_blob)
SQLITE_WRAPPER_INT(sqlite3_column_bytes)
SQLITE_WRAPPER_INT(sqlite3_column_text)
SQLITE_WRAPPER_INT(sqlite3_changes)
SQLITE_WRAPPER_INT(sqlite3_last_insert_rowid)
void setup_sqlite_functions(void *sqlite_handle)
{
#define GETFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(sqlite_handle, #name)
GETFUNC(sqlite3_open);
GETFUNC(sqlite3_errmsg);
GETFUNC(sqlite3_prepare_v2);
GETFUNC(sqlite3_bind_parameter_count);
GETFUNC(sqlite3_bind_null);
GETFUNC(sqlite3_bind_text);
GETFUNC(sqlite3_step);
GETFUNC(sqlite3_column_count);
GETFUNC(sqlite3_finalize);
GETFUNC(sqlite3_close);
GETFUNC(sqlite3_column_name);
GETFUNC(sqlite3_column_type);
GETFUNC(sqlite3_column_blob);
GETFUNC(sqlite3_column_bytes);
GETFUNC(sqlite3_column_text);
GETFUNC(sqlite3_changes);
GETFUNC(sqlite3_last_insert_rowid);
#undef GETFUNC
}
static bool initialized = false;
static jclass stringClass;
static jclass objectClass;
static jclass byteBufferClass;
static jclass cursorClass;
static jmethodID jByteBufferAllocateDirect;
static jmethodID jCursorConstructor;
static jmethodID jCursorAddRow;
static jobject sqliteInternalCall(JNIEnv* jenv, sqlite3 *db, jstring jQuery,
jobjectArray jParams, jlongArray jQueryRes);
static void throwSqliteException(JNIEnv* jenv, const char* aFormat, ...)
{
va_list ap;
va_start(ap, aFormat);
char* msg = nullptr;
vasprintf(&msg, aFormat, ap);
LOG("Error in SQLiteBridge: %s\n", msg);
JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException", msg);
free(msg);
va_end(ap);
}
static void
JNI_Setup(JNIEnv* jenv)
{
if (initialized) return;
jclass lObjectClass = jenv->FindClass("java/lang/Object");
jclass lStringClass = jenv->FindClass("java/lang/String");
jclass lByteBufferClass = jenv->FindClass("java/nio/ByteBuffer");
jclass lCursorClass = jenv->FindClass("org/mozilla/gecko/sqlite/MatrixBlobCursor");
if (lStringClass == nullptr
|| lObjectClass == nullptr
|| lByteBufferClass == nullptr
|| lCursorClass == nullptr) {
throwSqliteException(jenv, "FindClass error");
return;
}
// Those are only local references. Make them global so they work
// across calls and threads.
objectClass = (jclass)jenv->NewGlobalRef(lObjectClass);
stringClass = (jclass)jenv->NewGlobalRef(lStringClass);
byteBufferClass = (jclass)jenv->NewGlobalRef(lByteBufferClass);
cursorClass = (jclass)jenv->NewGlobalRef(lCursorClass);
if (stringClass == nullptr || objectClass == nullptr
|| byteBufferClass == nullptr
|| cursorClass == nullptr) {
throwSqliteException(jenv, "NewGlobalRef error");
return;
}
// public static ByteBuffer allocateDirect(int capacity)
jByteBufferAllocateDirect =
jenv->GetStaticMethodID(byteBufferClass, "allocateDirect", "(I)Ljava/nio/ByteBuffer;");
// new MatrixBlobCursor(String [])
jCursorConstructor =
jenv->GetMethodID(cursorClass, "<init>", "([Ljava/lang/String;)V");
// public void addRow (Object[] columnValues)
jCursorAddRow =
jenv->GetMethodID(cursorClass, "addRow", "([Ljava/lang/Object;)V");
if (jByteBufferAllocateDirect == nullptr
|| jCursorConstructor == nullptr
|| jCursorAddRow == nullptr) {
throwSqliteException(jenv, "GetMethodId error");
return;
}
initialized = true;
}
extern "C" NS_EXPORT jobject MOZ_JNICALL
Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass,
jstring jDb,
jstring jQuery,
jobjectArray jParams,
jlongArray jQueryRes)
{
JNI_Setup(jenv);
int rc;
jobject jCursor = nullptr;
const char* dbPath;
sqlite3 *db;
dbPath = jenv->GetStringUTFChars(jDb, nullptr);
rc = f_sqlite3_open(dbPath, &db);
jenv->ReleaseStringUTFChars(jDb, dbPath);
if (rc != SQLITE_OK) {
throwSqliteException(jenv,
"Can't open database: %s", f_sqlite3_errmsg(db));
f_sqlite3_close(db); // close db even if open failed
return nullptr;
}
jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes);
f_sqlite3_close(db);
return jCursor;
}
extern "C" NS_EXPORT jobject MOZ_JNICALL
Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCallWithDb(JNIEnv* jenv, jclass,
jlong jDb,
jstring jQuery,
jobjectArray jParams,
jlongArray jQueryRes)
{
JNI_Setup(jenv);
jobject jCursor = nullptr;
sqlite3 *db = (sqlite3*)jDb;
jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes);
return jCursor;
}
extern "C" NS_EXPORT jlong MOZ_JNICALL
Java_org_mozilla_gecko_sqlite_SQLiteBridge_openDatabase(JNIEnv* jenv, jclass,
jstring jDb)
{
JNI_Setup(jenv);
int rc;
const char* dbPath;
sqlite3 *db;
dbPath = jenv->GetStringUTFChars(jDb, nullptr);
rc = f_sqlite3_open(dbPath, &db);
jenv->ReleaseStringUTFChars(jDb, dbPath);
if (rc != SQLITE_OK) {
throwSqliteException(jenv,
"Can't open database: %s", f_sqlite3_errmsg(db));
f_sqlite3_close(db); // close db even if open failed
return 0;
}
return (jlong)db;
}
extern "C" NS_EXPORT void MOZ_JNICALL
Java_org_mozilla_gecko_sqlite_SQLiteBridge_closeDatabase(JNIEnv* jenv, jclass,
jlong jDb)
{
JNI_Setup(jenv);
sqlite3 *db = (sqlite3*)jDb;
f_sqlite3_close(db);
}
static jobject
sqliteInternalCall(JNIEnv* jenv,
sqlite3 *db,
jstring jQuery,
jobjectArray jParams,
jlongArray jQueryRes)
{
JNI_Setup(jenv);
jobject jCursor = nullptr;
jsize numPars = 0;
const char *pzTail;
sqlite3_stmt *ppStmt;
int rc;
const char* queryStr;
queryStr = jenv->GetStringUTFChars(jQuery, nullptr);
rc = f_sqlite3_prepare_v2(db, queryStr, -1, &ppStmt, &pzTail);
if (rc != SQLITE_OK || ppStmt == nullptr) {
throwSqliteException(jenv,
"Can't prepare statement: %s", f_sqlite3_errmsg(db));
return nullptr;
}
jenv->ReleaseStringUTFChars(jQuery, queryStr);
// Check if number of parameters matches
if (jParams != nullptr) {
numPars = jenv->GetArrayLength(jParams);
}
int sqlNumPars;
sqlNumPars = f_sqlite3_bind_parameter_count(ppStmt);
if (numPars != sqlNumPars) {
throwSqliteException(jenv,
"Passed parameter count (%d) "
"doesn't match SQL parameter count (%d)",
numPars, sqlNumPars);
return nullptr;
}
if (jParams != nullptr) {
// Bind parameters, if any
if (numPars > 0) {
for (int i = 0; i < numPars; i++) {
jobject jObjectParam = jenv->GetObjectArrayElement(jParams, i);
// IsInstanceOf or isAssignableFrom? String is final, so IsInstanceOf
// should be OK.
jboolean isString = jenv->IsInstanceOf(jObjectParam, stringClass);
if (isString != JNI_TRUE) {
throwSqliteException(jenv,
"Parameter is not of String type");
return nullptr;
}
// SQLite parameters index from 1.
if (jObjectParam == nullptr) {
rc = f_sqlite3_bind_null(ppStmt, i + 1);
} else {
jstring jStringParam = (jstring) jObjectParam;
const char* paramStr = jenv->GetStringUTFChars(jStringParam, nullptr);
rc = f_sqlite3_bind_text(ppStmt, i + 1, paramStr, -1, SQLITE_TRANSIENT);
jenv->ReleaseStringUTFChars(jStringParam, paramStr);
}
if (rc != SQLITE_OK) {
throwSqliteException(jenv, "Error binding query parameter");
return nullptr;
}
}
}
}
// Execute the query and step through the results
rc = f_sqlite3_step(ppStmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
throwSqliteException(jenv,
"Can't step statement: (%d) %s", rc, f_sqlite3_errmsg(db));
return nullptr;
}
// Get the column count and names
int cols;
cols = f_sqlite3_column_count(ppStmt);
{
// Allocate a String[cols]
jobjectArray jStringArray = jenv->NewObjectArray(cols,
stringClass,
nullptr);
if (jStringArray == nullptr) {
throwSqliteException(jenv, "Can't allocate String[]");
return nullptr;
}
// Assign column names to the String[]
for (int i = 0; i < cols; i++) {
const char* colName = f_sqlite3_column_name(ppStmt, i);
jstring jStr = jenv->NewStringUTF(colName);
jenv->SetObjectArrayElement(jStringArray, i, jStr);
}
// Construct the MatrixCursor(String[]) with given column names
jCursor = jenv->NewObject(cursorClass,
jCursorConstructor,
jStringArray);
if (jCursor == nullptr) {
throwSqliteException(jenv, "Can't allocate MatrixBlobCursor");
return nullptr;
}
}
// Return the id and number of changed rows in jQueryRes
{
jlong id = f_sqlite3_last_insert_rowid(db);
jenv->SetLongArrayRegion(jQueryRes, 0, 1, &id);
jlong changed = f_sqlite3_changes(db);
jenv->SetLongArrayRegion(jQueryRes, 1, 1, &changed);
}
// For each row, add an Object[] to the passed ArrayList,
// with that containing either String or ByteArray objects
// containing the columns
while (rc != SQLITE_DONE) {
// Process row
// Construct Object[]
jobjectArray jRow = jenv->NewObjectArray(cols,
objectClass,
nullptr);
if (jRow == nullptr) {
throwSqliteException(jenv, "Can't allocate jRow Object[]");
return nullptr;
}
for (int i = 0; i < cols; i++) {
int colType = f_sqlite3_column_type(ppStmt, i);
if (colType == SQLITE_BLOB) {
// Treat as blob
const void* blob = f_sqlite3_column_blob(ppStmt, i);
int colLen = f_sqlite3_column_bytes(ppStmt, i);
// Construct ByteBuffer of correct size
jobject jByteBuffer =