Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Fixes

- Prevent usage of internal UE logger during crash handling ([#1081](https://github.com/getsentry/sentry-unreal/pull/1081))
- Crash when printing to logs from multiple threads on Android ([#1092](https://github.com/getsentry/sentry-unreal/pull/1092))

### Dependencies

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@
#include "Misc/OutputDeviceError.h"
#include "Serialization/JsonSerializer.h"

FAndroidSentrySubsystem::FAndroidSentrySubsystem()
{
SentryJavaClasses::InitJavaClassRefsCache();
}

FAndroidSentrySubsystem::~FAndroidSentrySubsystem()
{
SentryJavaClasses::ClearJavaClassRefsCache();
}

void FAndroidSentrySubsystem::InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryBeforeBreadcrumbHandler* beforeBreadcrumbHandler, USentryTraceSampler* traceSampler)
{
TSharedPtr<FJsonObject> SettingsJson = MakeShareable(new FJsonObject);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
class FAndroidSentrySubsystem : public ISentrySubsystem
{
public:
FAndroidSentrySubsystem();
~FAndroidSentrySubsystem();

virtual void InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryBeforeBreadcrumbHandler* beforeBreadcrumbHandler, USentryTraceSampler* traceSampler) override;
virtual void Close() override;
virtual bool IsEnabled() override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ TSharedPtr<FSentryJavaObjectWrapper> FAndroidSentryConverters::SentryLevelToNati

JNIEnv* Env = AndroidJavaEnv::GetJavaEnv();

jclass levelEnumClass = AndroidJavaEnv::FindJavaClassGlobalRef("io/sentry/SentryLevel");
jclass levelEnumClass = SentryJavaClasses::GetCachedJavaClassRef(SentryJavaClasses::SentryLevel);

jfieldID debugEnumFieldField = Env->GetStaticFieldID(levelEnumClass, "DEBUG", "Lio/sentry/SentryLevel;");
jfieldID infoEnumFieldField = Env->GetStaticFieldID(levelEnumClass, "INFO", "Lio/sentry/SentryLevel;");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#pragma once

#include "CoreMinimal.h"

#include "Android/AndroidJNI.h"

enum class ESentryJavaClassType : uint8
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "AndroidSentryJavaClasses.h"

#include "Android/AndroidJavaEnv.h"

// clang-format off

// External Java classes definitions
Expand Down Expand Up @@ -40,4 +42,85 @@ const FSentryJavaClass SentryJavaClasses::Float = FSentryJavaClass { "java/l
const FSentryJavaClass SentryJavaClasses::Boolean = FSentryJavaClass { "java/lang/Boolean", ESentryJavaClassType::System };
const FSentryJavaClass SentryJavaClasses::String = FSentryJavaClass { "java/lang/String", ESentryJavaClassType::System };

// clang-format on
// clang-format on

TMap<FName, jclass> SentryJavaClasses::JavaClassRefsCache;

void SentryJavaClasses::InitJavaClassRefsCache()
{
// External Java classes definitions
JavaClassRefsCache.Add(SentryBridgeJava.Name, FindJavaClassRef(SentryBridgeJava));
JavaClassRefsCache.Add(Sentry.Name, FindJavaClassRef(Sentry));
JavaClassRefsCache.Add(Attachment.Name, FindJavaClassRef(Attachment));
JavaClassRefsCache.Add(Breadcrumb.Name, FindJavaClassRef(Breadcrumb));
JavaClassRefsCache.Add(SentryEvent.Name, FindJavaClassRef(SentryEvent));
JavaClassRefsCache.Add(SentryId.Name, FindJavaClassRef(SentryId));
JavaClassRefsCache.Add(Scope.Name, FindJavaClassRef(Scope));
JavaClassRefsCache.Add(ScopeImpl.Name, FindJavaClassRef(ScopeImpl));
JavaClassRefsCache.Add(User.Name, FindJavaClassRef(User));
JavaClassRefsCache.Add(Feedback.Name, FindJavaClassRef(Feedback));
JavaClassRefsCache.Add(Message.Name, FindJavaClassRef(Message));
JavaClassRefsCache.Add(SentryLevel.Name, FindJavaClassRef(SentryLevel));
JavaClassRefsCache.Add(SentryHint.Name, FindJavaClassRef(SentryHint));
JavaClassRefsCache.Add(Transaction.Name, FindJavaClassRef(Transaction));
JavaClassRefsCache.Add(Span.Name, FindJavaClassRef(Span));
JavaClassRefsCache.Add(SamplingContext.Name, FindJavaClassRef(SamplingContext));
JavaClassRefsCache.Add(CustomSamplingContext.Name, FindJavaClassRef(CustomSamplingContext));
JavaClassRefsCache.Add(TransactionContext.Name, FindJavaClassRef(TransactionContext));
JavaClassRefsCache.Add(TransactionOptions.Name, FindJavaClassRef(TransactionOptions));
JavaClassRefsCache.Add(SentryTraceHeader.Name, FindJavaClassRef(SentryTraceHeader));

// System Java classes definitions
JavaClassRefsCache.Add(ArrayList.Name, FindJavaClassRef(ArrayList));
JavaClassRefsCache.Add(HashMap.Name, FindJavaClassRef(HashMap));
JavaClassRefsCache.Add(Map.Name, FindJavaClassRef(Map));
JavaClassRefsCache.Add(Set.Name, FindJavaClassRef(Set));
JavaClassRefsCache.Add(Iterator.Name, FindJavaClassRef(Iterator));
JavaClassRefsCache.Add(MapEntry.Name, FindJavaClassRef(MapEntry));
JavaClassRefsCache.Add(List.Name, FindJavaClassRef(List));
JavaClassRefsCache.Add(Double.Name, FindJavaClassRef(Double));
JavaClassRefsCache.Add(Integer.Name, FindJavaClassRef(Integer));
JavaClassRefsCache.Add(Float.Name, FindJavaClassRef(Float));
JavaClassRefsCache.Add(Boolean.Name, FindJavaClassRef(Boolean));
JavaClassRefsCache.Add(String.Name, FindJavaClassRef(String));
}

void SentryJavaClasses::ClearJavaClassRefsCache()
{
JNIEnv* JEnv = AndroidJavaEnv::GetJavaEnv();

for (auto& CachedItem : JavaClassRefsCache)
{
if (CachedItem.Value)
{
JEnv->DeleteGlobalRef(CachedItem.Value);
}
}

JavaClassRefsCache.Empty();
}

jclass SentryJavaClasses::GetCachedJavaClassRef(const FSentryJavaClass& ClassData)
{
jclass* Class = JavaClassRefsCache.Find(ClassData.Name);

checkf(Class && *Class, TEXT("Failed to retrieve Java class reference from cache for %s"), *ClassData.Name.ToString());
return *Class;
}

jclass SentryJavaClasses::FindJavaClassRef(const FSentryJavaClass& ClassData)
{
JNIEnv* JEnv = AndroidJavaEnv::GetJavaEnv();

ANSICHAR AnsiClassName[NAME_SIZE];
ClassData.Name.GetPlainANSIString(AnsiClassName);

jclass Class = nullptr;
if (ClassData.Type == ESentryJavaClassType::System)
Class = FJavaWrapper::FindClassGlobalRef(JEnv, AnsiClassName, false);
else if (ClassData.Type == ESentryJavaClassType::External)
Class = AndroidJavaEnv::FindJavaClassGlobalRef(AnsiClassName);

checkf(Class, TEXT("Failed to obtain Java class reference for %s"), *ClassData.Name.ToString());
return Class;
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,14 @@ struct SentryJavaClasses
const static FSentryJavaClass Float;
const static FSentryJavaClass Boolean;
const static FSentryJavaClass String;

// Java class references cache
static void InitJavaClassRefsCache();
static void ClearJavaClassRefsCache();

static jclass GetCachedJavaClassRef(const FSentryJavaClass& ClassData);
static jclass FindJavaClassRef(const FSentryJavaClass& ClassData);

private:
static TMap<FName, jclass> JavaClassRefsCache;
};
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
// Copyright (c) 2025 Sentry. All Rights Reserved.

#include "AndroidSentryJavaObjectWrapper.h"
#include "AndroidSentryJavaClasses.h"

#include "Android/AndroidJNI.h"

FSentryJavaObjectWrapper::FSentryJavaObjectWrapper(FSentryJavaClass ClassData)
: Object(nullptr)
, Class(nullptr)
, Class(SentryJavaClasses::GetCachedJavaClassRef(ClassData))
{
JNIEnv* JEnv = AndroidJavaEnv::GetJavaEnv();

ANSICHAR AnsiClassName[NAME_SIZE];
ClassData.Name.GetPlainANSIString(AnsiClassName);

if (ClassData.Type == ESentryJavaClassType::System)
Class = FJavaWrapper::FindClassGlobalRef(JEnv, AnsiClassName, false);
if (ClassData.Type == ESentryJavaClassType::External)
Class = AndroidJavaEnv::FindJavaClassGlobalRef(AnsiClassName);

check(Class);
}

FSentryJavaObjectWrapper::FSentryJavaObjectWrapper(FSentryJavaClass ClassData, const char* CtorSignature, ...)
Expand Down Expand Up @@ -58,8 +48,6 @@ FSentryJavaObjectWrapper::~FSentryJavaObjectWrapper()

if (Object)
JEnv->DeleteGlobalRef(Object);
if (Class)
JEnv->DeleteGlobalRef(Class);
}

FSentryJavaMethod FSentryJavaObjectWrapper::GetMethod(const char* MethodName, const char* FunctionSignature)
Expand All @@ -76,11 +64,11 @@ FSentryJavaMethod FSentryJavaObjectWrapper::GetMethod(const char* MethodName, co

FSentryJavaMethod FSentryJavaObjectWrapper::GetStaticMethod(FSentryJavaClass ClassData, const char* MethodName, const char* FunctionSignature)
{
FSentryJavaObjectWrapper StaticClass(ClassData);
jclass StaticClass = SentryJavaClasses::GetCachedJavaClassRef(ClassData);

JNIEnv* JEnv = AndroidJavaEnv::GetJavaEnv();
FSentryJavaMethod Method;
Method.Method = JEnv->GetStaticMethodID(StaticClass.Class, MethodName, FunctionSignature);
Method.Method = JEnv->GetStaticMethodID(StaticClass, MethodName, FunctionSignature);
Method.Name = MethodName;
Method.Signature = FunctionSignature;
Method.IsStatic = true;
Expand All @@ -103,17 +91,7 @@ FScopedJavaObject<jstring> FSentryJavaObjectWrapper::GetJString(const FString& S
bool FSentryJavaObjectWrapper::IsInstanceOf(FSentryJavaClass ClassData, jobject JavaClassInstance)
{
JNIEnv* JEnv = AndroidJavaEnv::GetJavaEnv();

ANSICHAR AnsiClassName[NAME_SIZE];
ClassData.Name.GetPlainANSIString(AnsiClassName);

jclass ClassGlobalRef;

if (ClassData.Type == ESentryJavaClassType::System)
ClassGlobalRef = FJavaWrapper::FindClassGlobalRef(JEnv, AnsiClassName, false);
if (ClassData.Type == ESentryJavaClassType::External)
ClassGlobalRef = AndroidJavaEnv::FindJavaClassGlobalRef(AnsiClassName);

jclass ClassGlobalRef = SentryJavaClasses::GetCachedJavaClassRef(ClassData);
check(ClassGlobalRef);

return JEnv->IsInstanceOf(JavaClassInstance, ClassGlobalRef);
Expand Down