Skip to content

Commit

Permalink
fix: Clean up JNI code and properly free strings (#1050)
Browse files Browse the repository at this point in the history
  • Loading branch information
Swatinem authored Nov 18, 2020
1 parent 01cfbfe commit 26f4693
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 94 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
* Added java doc to protocol classes based on sentry-data-schemes project (#1045)
* Enhancement: Make SentryExceptionResolver Order configurable to not send handled web exceptions (#1008)
* Enhancement: Resolve HTTP Proxy parameters from the external configuration (#1028)
* Fix: set userId for hard-crashes if no user is set
* Enhancement: Sentry NDK integration is compiled against default NDK version based on AGP's version
* Enhancement: Sentry NDK integration is compiled against default NDK version based on AGP's version #1048
* Fix: Clean up JNI code and properly free strings #1050
* Fix: set userId for hard-crashes if no user is set #1049

# 3.1.3

Expand Down
238 changes: 146 additions & 92 deletions sentry-android-ndk/src/main/jni/sentry.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,210 +2,264 @@
#include <sentry.h>
#include <jni.h>

struct transport_options {
jclass cls;
JNIEnv *env;
char outbox_path[4096];
int debug;
};

struct transport_options g_transport_options;
#define ENSURE(Expr) \
if (!(Expr)) \
return

JNIEXPORT void JNICALL
Java_io_sentry_android_ndk_NativeScope_nativeSetTag(JNIEnv *env, jclass cls, jstring key,
jstring value) {
jstring value)
{
const char *charKey = (*env)->GetStringUTFChars(env, key, 0);
const char *charValue = (*env)->GetStringUTFChars(env, value, 0);

sentry_set_tag(charKey, charValue);

(*env)->ReleaseStringUTFChars(env, key, charKey);
(*env)->ReleaseStringUTFChars(env, value, charValue);
}

JNIEXPORT void JNICALL
Java_io_sentry_android_ndk_NativeScope_nativeRemoveTag(JNIEnv *env, jclass cls, jstring key) {
Java_io_sentry_android_ndk_NativeScope_nativeRemoveTag(JNIEnv *env, jclass cls, jstring key)
{
const char *charKey = (*env)->GetStringUTFChars(env, key, 0);

sentry_remove_tag(charKey);

(*env)->ReleaseStringUTFChars(env, key, charKey);
}

JNIEXPORT void JNICALL
Java_io_sentry_android_ndk_NativeScope_nativeSetExtra(JNIEnv *env, jclass cls, jstring key,
jstring value) {
jstring value)
{
const char *charKey = (*env)->GetStringUTFChars(env, key, 0);
const char *charValue = (*env)->GetStringUTFChars(env, value, 0);

sentry_value_t sentryValue = sentry_value_new_string(charValue);
sentry_set_extra(charKey, sentryValue);

(*env)->ReleaseStringUTFChars(env, key, charKey);
(*env)->ReleaseStringUTFChars(env, value, charValue);
}

JNIEXPORT void JNICALL
Java_io_sentry_android_ndk_NativeScope_nativeRemoveExtra(JNIEnv *env, jclass cls,
jstring key) {
jstring key)
{
const char *charKey = (*env)->GetStringUTFChars(env, key, 0);

sentry_remove_extra(charKey);

(*env)->ReleaseStringUTFChars(env, key, charKey);
}

JNIEXPORT void JNICALL Java_io_sentry_android_ndk_NativeScope_nativeSetUser(
JNIEnv *env,
jclass cls,
jstring id,
jstring email,
jstring ipAddress,
jstring username) {
JNIEnv *env,
jclass cls,
jstring id,
jstring email,
jstring ipAddress,
jstring username)
{
sentry_value_t user = sentry_value_new_object();
if (id) {
if (id)
{
const char *charId = (*env)->GetStringUTFChars(env, id, 0);
sentry_value_set_by_key(user, "id", sentry_value_new_string(charId));
(*env)->ReleaseStringUTFChars(env, id, charId);
}
if (email) {
if (email)
{
const char *charEmail = (*env)->GetStringUTFChars(env, email, 0);
sentry_value_set_by_key(
user, "email", sentry_value_new_string(charEmail));
user, "email", sentry_value_new_string(charEmail));
(*env)->ReleaseStringUTFChars(env, email, charEmail);
}
if (ipAddress) {
if (ipAddress)
{
const char *charIpAddress = (*env)->GetStringUTFChars(env, ipAddress, 0);
sentry_value_set_by_key(
user, "ip_address", sentry_value_new_string(charIpAddress));
user, "ip_address", sentry_value_new_string(charIpAddress));
(*env)->ReleaseStringUTFChars(env, ipAddress, charIpAddress);
}
if (username) {
if (username)
{
const char *charUsername = (*env)->GetStringUTFChars(env, username, 0);
sentry_value_set_by_key(
user, "username", sentry_value_new_string(charUsername));
user, "username", sentry_value_new_string(charUsername));
(*env)->ReleaseStringUTFChars(env, username, charUsername);
}
sentry_set_user(user);
}

JNIEXPORT void JNICALL Java_io_sentry_android_ndk_NativeScope_nativeRemoveUser(
JNIEnv *env,
jclass cls) {
JNIEnv *env,
jclass cls)
{
sentry_remove_user();
}

JNIEXPORT void JNICALL Java_io_sentry_android_ndk_NativeScope_nativeAddBreadcrumb(
JNIEnv *env,
jclass cls,
jstring level,
jstring message,
jstring category,
jstring type,
jstring timestamp,
jstring data) {
if (!level && !message && !category && !type) {
JNIEnv *env,
jclass cls,
jstring level,
jstring message,
jstring category,
jstring type,
jstring timestamp,
jstring data)
{
if (!level && !message && !category && !type)
{
return;
}
const char *charMessage = NULL;
if (message) {
if (message)
{
charMessage = (*env)->GetStringUTFChars(env, message, 0);
}
const char *charType = NULL;
if (type) {
if (type)
{
charType = (*env)->GetStringUTFChars(env, type, 0);
}
sentry_value_t crumb
= sentry_value_new_breadcrumb(charType, charMessage);
sentry_value_t crumb = sentry_value_new_breadcrumb(charType, charMessage);

if (category) {
if (charMessage)
{
(*env)->ReleaseStringUTFChars(env, message, charMessage);
}
if (charType)
{
(*env)->ReleaseStringUTFChars(env, type, charType);
}

if (category)
{
const char *charCategory = (*env)->GetStringUTFChars(env, category, 0);
sentry_value_set_by_key(
crumb, "category", sentry_value_new_string(charCategory));
crumb, "category", sentry_value_new_string(charCategory));
(*env)->ReleaseStringUTFChars(env, category, charCategory);
}
if (level) {
if (level)
{
const char *charLevel = (*env)->GetStringUTFChars(env, level, 0);
sentry_value_set_by_key(
crumb, "level", sentry_value_new_string(charLevel));
crumb, "level", sentry_value_new_string(charLevel));
(*env)->ReleaseStringUTFChars(env, level, charLevel);
}

if (timestamp) {
if (timestamp)
{
// overwrite timestamp that is already created on sentry_value_new_breadcrumb
const char *charTimestamp = (*env)->GetStringUTFChars(env, timestamp, 0);
sentry_value_set_by_key(
crumb, "timestamp", sentry_value_new_string(charTimestamp));
crumb, "timestamp", sentry_value_new_string(charTimestamp));
(*env)->ReleaseStringUTFChars(env, timestamp, charTimestamp);
}

if (data) {
if (data)
{
const char *charData = (*env)->GetStringUTFChars(env, data, 0);

// we create an object because the Java layer parses it as a Map
sentry_value_t dataObject = sentry_value_new_object();
sentry_value_set_by_key(dataObject, "data", sentry_value_new_string(charData));

sentry_value_set_by_key(crumb, "data", dataObject);

(*env)->ReleaseStringUTFChars(env, data, charData);
}

sentry_add_breadcrumb(crumb);
}

static void send_envelope(const sentry_envelope_t *envelope, void *unused_data) {
(void) unused_data;
static void send_envelope(sentry_envelope_t *envelope, void *data)
{
const char *outbox_path = (const char *)data;
char envelope_id_str[40];
char outbox_path[4096];
char envelope_path[4096];

sentry_uuid_t envelope_id = sentry_uuid_new_v4();
sentry_uuid_as_string(&envelope_id, envelope_id_str);

strcpy(outbox_path, g_transport_options.outbox_path);
strcat(outbox_path, "/");
strcat(outbox_path, envelope_id_str);
sentry_envelope_write_to_file(envelope, outbox_path);
strcpy(envelope_path, outbox_path);
strcat(envelope_path, "/");
strcat(envelope_path, envelope_id_str);

sentry_envelope_write_to_file(envelope, envelope_path);

sentry_envelope_free(envelope);
}

JNIEXPORT void JNICALL
Java_io_sentry_android_ndk_SentryNdk_initSentryNative(JNIEnv *env, jclass cls,
jobject sentry_sdk_options) {
jobject sentry_sdk_options)
{
jclass options_cls = (*env)->GetObjectClass(env, sentry_sdk_options);

jmethodID outbox_path_mid = (*env)->GetMethodID(env, options_cls, "getOutboxPath",
"()Ljava/lang/String;");
jstring outbox_path = (jstring) (*env)->CallObjectMethod(env, sentry_sdk_options,
outbox_path_mid);
strncpy(g_transport_options.outbox_path, (*env)->GetStringUTFChars(env, outbox_path, 0),
sizeof(g_transport_options.outbox_path));

jmethodID outbox_path_mid = (*env)->GetMethodID(env, options_cls, "getOutboxPath", "()Ljava/lang/String;");
jmethodID dsn_mid = (*env)->GetMethodID(env, options_cls, "getDsn", "()Ljava/lang/String;");
jstring dsn = (jstring) (*env)->CallObjectMethod(env, sentry_sdk_options, dsn_mid);

jmethodID is_debug_mid = (*env)->GetMethodID(env, options_cls, "isDebug", "()Z");
g_transport_options.debug = (*env)->CallBooleanMethod(env, sentry_sdk_options, is_debug_mid);

jmethodID release_mid = (*env)->GetMethodID(env, options_cls, "getRelease",
"()Ljava/lang/String;");
jstring release = (jstring) (*env)->CallObjectMethod(env, sentry_sdk_options, release_mid);

jmethodID environment_mid = (*env)->GetMethodID(env, options_cls, "getEnvironment",
"()Ljava/lang/String;");
jstring environment = (jstring) (*env)->CallObjectMethod(env, sentry_sdk_options,
environment_mid);

jmethodID release_mid = (*env)->GetMethodID(env, options_cls, "getRelease", "()Ljava/lang/String;");
jmethodID environment_mid = (*env)->GetMethodID(env, options_cls, "getEnvironment", "()Ljava/lang/String;");
jmethodID dist_mid = (*env)->GetMethodID(env, options_cls, "getDist", "()Ljava/lang/String;");
jstring dist = (jstring) (*env)->CallObjectMethod(env, sentry_sdk_options, dist_mid);

g_transport_options.env = env;
g_transport_options.cls = cls;
jstring outbox_path_j = (jstring)(*env)->CallObjectMethod(env, sentry_sdk_options, outbox_path_mid);
jstring dsn = (jstring)(*env)->CallObjectMethod(env, sentry_sdk_options, dsn_mid);
jboolean debug = (jboolean)(*env)->CallBooleanMethod(env, sentry_sdk_options, is_debug_mid);
jstring release = (jstring)(*env)->CallObjectMethod(env, sentry_sdk_options, release_mid);
jstring environment = (jstring)(*env)->CallObjectMethod(env, sentry_sdk_options, environment_mid);
jstring dist = (jstring)(*env)->CallObjectMethod(env, sentry_sdk_options, dist_mid);

ENSURE(outbox_path_j);
const char *outbox_path_str = (*env)->GetStringUTFChars(env, outbox_path_j, 0);
ENSURE(outbox_path_str);
char *outbox_path = strdup(outbox_path_str);
ENSURE(outbox_path);
(*env)->ReleaseStringUTFChars(env, outbox_path_j, outbox_path_str);

sentry_transport_t *transport = sentry_transport_new(send_envelope);
ENSURE(transport);
sentry_transport_set_state(transport, outbox_path);
sentry_transport_set_free_func(transport, sentry_free);

sentry_options_t *options = sentry_options_new();
ENSURE(options);

// give sentry-native its own database path it can work with, next to the outbox
char database_path[4096];
strncpy(database_path, g_transport_options.outbox_path, 4096);
strncpy(database_path, outbox_path, 4096);
char *dir = strrchr(database_path, '/');
if (dir) {
if (dir)
{
strncpy(dir + 1, ".sentry-native", 4096 - (dir + 1 - database_path));
}
sentry_options_set_database_path(options, database_path);

sentry_options_set_transport(
options, sentry_new_function_transport(send_envelope, NULL));
sentry_options_set_debug(options, g_transport_options.debug);
sentry_options_set_dsn(options, (*env)->GetStringUTFChars(env, dsn, 0));

if (release) {
sentry_options_set_release(options, (*env)->GetStringUTFChars(env, release, 0));
sentry_options_set_transport(options, transport);
sentry_options_set_debug(options, debug);
const char *dsn_str = (*env)->GetStringUTFChars(env, dsn, 0);
sentry_options_set_dsn(options, dsn_str);
(*env)->ReleaseStringUTFChars(env, dsn, dsn_str);

if (release)
{
const char *release_str = (*env)->GetStringUTFChars(env, release, 0);
sentry_options_set_release(options, release_str);
(*env)->ReleaseStringUTFChars(env, release, release_str);
}
if (environment) {
sentry_options_set_environment(options, (*env)->GetStringUTFChars(env, environment, 0));
if (environment)
{
const char *environment_str = (*env)->GetStringUTFChars(env, environment, 0);
sentry_options_set_environment(options, environment_str);
(*env)->ReleaseStringUTFChars(env, environment, environment_str);
}
if (dist) {
sentry_options_set_dist(options, (*env)->GetStringUTFChars(env, dist, 0));
if (dist)
{
const char *dist_str = (*env)->GetStringUTFChars(env, dist, 0);
sentry_options_set_dist(options, dist_str);
(*env)->ReleaseStringUTFChars(env, dist, dist_str);
}
// session tracking is enabled by default, but the Android SDK already handles it
sentry_options_set_auto_session_tracking(options, 0);
Expand Down

0 comments on commit 26f4693

Please sign in to comment.