// Copyright (c) 2022 Sentry. All Rights Reserved. #include "SentrySubsystemAndroid.h" #include "SentryEventAndroid.h" #include "SentryBreadcrumbAndroid.h" #include "SentryUserFeedbackAndroid.h" #include "SentryUserAndroid.h" #include "SentryTransactionAndroid.h" #include "SentryTransactionContextAndroid.h" #include "SentryTransactionOptionsAndroid.h" #include "SentryIdAndroid.h" #include "SentryDefines.h" #include "SentryBeforeSendHandler.h" #include "SentryTraceSampler.h" #include "SentryEvent.h" #include "SentrySettings.h" #include "Callbacks/SentryScopeCallbackAndroid.h" #include "Infrastructure/SentryConvertorsAndroid.h" #include "Infrastructure/SentryJavaClasses.h" #include "Utils/SentryFileUtils.h" #include "Dom/JsonObject.h" #include "Serialization/JsonSerializer.h" void SentrySubsystemAndroid::InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryTraceSampler* traceSampler) { TSharedPtr SettingsJson = MakeShareable(new FJsonObject); SettingsJson->SetStringField(TEXT("dsn"), settings->Dsn); SettingsJson->SetStringField(TEXT("release"), settings->OverrideReleaseName ? settings->Release : settings->GetFormattedReleaseName()); SettingsJson->SetStringField(TEXT("environment"), settings->Environment); SettingsJson->SetBoolField(TEXT("autoSessionTracking"), settings->EnableAutoSessionTracking); SettingsJson->SetNumberField(TEXT("sessionTimeout"), settings->SessionTimeout); SettingsJson->SetBoolField(TEXT("enableStackTrace"), settings->AttachStacktrace); SettingsJson->SetBoolField(TEXT("debug"), settings->Debug); SettingsJson->SetNumberField(TEXT("sampleRate"), settings->SampleRate); SettingsJson->SetNumberField(TEXT("maxBreadcrumbs"), settings->MaxBreadcrumbs); SettingsJson->SetBoolField(TEXT("attachScreenshot"), settings->AttachScreenshot); SettingsJson->SetArrayField(TEXT("inAppInclude"), SentryConvertorsAndroid::StrinArrayToJsonArray(settings->InAppInclude)); SettingsJson->SetArrayField(TEXT("inAppExclude"), SentryConvertorsAndroid::StrinArrayToJsonArray(settings->InAppExclude)); SettingsJson->SetBoolField(TEXT("sendDefaultPii"), settings->SendDefaultPii); SettingsJson->SetBoolField(TEXT("enableAnrTracking"), settings->EnableAppNotRespondingTracking); SettingsJson->SetBoolField(TEXT("enableTracing"), settings->EnableTracing); if(settings->EnableTracing && settings->SamplingType == ESentryTracesSamplingType::UniformSampleRate) { SettingsJson->SetNumberField(TEXT("tracesSampleRate"), settings->TracesSampleRate); } if(settings->EnableTracing && settings->SamplingType == ESentryTracesSamplingType::TracesSampler) { SettingsJson->SetNumberField(TEXT("tracesSampler"), (jlong)traceSampler); } FString SettingsJsonStr; TSharedRef> JsonWriter = TJsonWriterFactory<>::Create(&SettingsJsonStr); FJsonSerializer::Serialize(SettingsJson.ToSharedRef(), JsonWriter); FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::SentryBridgeJava, "init", "(Landroid/app/Activity;Ljava/lang/String;J)V", FJavaWrapper::GameActivityThis, *FSentryJavaObjectWrapper::GetJString(SettingsJsonStr), (jlong)beforeSendHandler); } void SentrySubsystemAndroid::Close() { FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::Sentry, "close", "()V"); } bool SentrySubsystemAndroid::IsEnabled() { return FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::Sentry, "isEnabled", "()Z"); } ESentryCrashedLastRun SentrySubsystemAndroid::IsCrashedLastRun() { ESentryCrashedLastRun unrealIsCrashed = ESentryCrashedLastRun::NotEvaluated; switch (FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::SentryBridgeJava, "isCrashedLastRun", "()I")) { case -1: unrealIsCrashed = ESentryCrashedLastRun::NotEvaluated; break; case 0: unrealIsCrashed = ESentryCrashedLastRun::NotCrashed; break; case 1: unrealIsCrashed = ESentryCrashedLastRun::Crashed; break; default: UE_LOG(LogSentrySdk, Warning, TEXT("Unknown IsCrashedLastRun result. NotEvaluated will be returned.")); } return unrealIsCrashed; } void SentrySubsystemAndroid::AddBreadcrumb(TSharedPtr breadcrumb) { TSharedPtr breadcrumbAndroid = StaticCastSharedPtr(breadcrumb); FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::Sentry, "addBreadcrumb", "(Lio/sentry/Breadcrumb;)V", breadcrumbAndroid->GetJObject()); } void SentrySubsystemAndroid::AddBreadcrumbWithParams(const FString& Message, const FString& Category, const FString& Type, const TMap& Data, ESentryLevel Level) { TSharedPtr breadcrumbAndroid = MakeShareable(new SentryBreadcrumbAndroid()); breadcrumbAndroid->SetMessage(Message); breadcrumbAndroid->SetCategory(Category); breadcrumbAndroid->SetType(Type); breadcrumbAndroid->SetData(Data); breadcrumbAndroid->SetLevel(Level); FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::Sentry, "addBreadcrumb", "(Lio/sentry/Breadcrumb;)V", breadcrumbAndroid->GetJObject()); } void SentrySubsystemAndroid::ClearBreadcrumbs() { FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::Sentry, "clearBreadcrumbs", "()V"); } TSharedPtr SentrySubsystemAndroid::CaptureMessage(const FString& message, ESentryLevel level) { auto id = FSentryJavaObjectWrapper::CallStaticObjectMethod(SentryJavaClasses::Sentry, "captureMessage", "(Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId;", *FSentryJavaObjectWrapper::GetJString(message), SentryConvertorsAndroid::SentryLevelToNative(level)->GetJObject()); return MakeShareable(new SentryIdAndroid(*id)); } TSharedPtr SentrySubsystemAndroid::CaptureMessageWithScope(const FString& message, const FSentryScopeDelegate& onConfigureScope, ESentryLevel level) { int64 scopeCallbackId = SentryScopeCallbackAndroid::SaveDelegate(onConfigureScope); auto id = FSentryJavaObjectWrapper::CallStaticObjectMethod(SentryJavaClasses::SentryBridgeJava, "captureMessageWithScope", "(Ljava/lang/String;Lio/sentry/SentryLevel;J)Lio/sentry/protocol/SentryId;", *FSentryJavaObjectWrapper::GetJString(message), SentryConvertorsAndroid::SentryLevelToNative(level)->GetJObject(), scopeCallbackId); return MakeShareable(new SentryIdAndroid(*id)); } TSharedPtr SentrySubsystemAndroid::CaptureEvent(TSharedPtr event) { TSharedPtr eventAndroid = StaticCastSharedPtr(event); auto id = FSentryJavaObjectWrapper::CallStaticObjectMethod(SentryJavaClasses::Sentry, "captureEvent", "(Lio/sentry/SentryEvent;)Lio/sentry/protocol/SentryId;", eventAndroid->GetJObject()); return MakeShareable(new SentryIdAndroid(*id)); } TSharedPtr SentrySubsystemAndroid::CaptureEventWithScope(TSharedPtr event, const FSentryScopeDelegate& onConfigureScope) { TSharedPtr eventAndroid = StaticCastSharedPtr(event); int64 scopeCallbackId = SentryScopeCallbackAndroid::SaveDelegate(onConfigureScope); auto id = FSentryJavaObjectWrapper::CallStaticObjectMethod(SentryJavaClasses::SentryBridgeJava, "captureEventWithScope", "(Lio/sentry/SentryEvent;J)Lio/sentry/protocol/SentryId;", eventAndroid->GetJObject(), scopeCallbackId); return MakeShareable(new SentryIdAndroid(*id)); } TSharedPtr SentrySubsystemAndroid::CaptureException(const FString& type, const FString& message, int32 framesToSkip) { return nullptr; } TSharedPtr SentrySubsystemAndroid::CaptureAssertion(const FString& type, const FString& message) { const int32 framesToSkip = 8; // add marker tags specific for Unreal assertions SetTag(TEXT("sentry_unreal_exception"), TEXT("assert")); SetTag(TEXT("sentry_unreal_exception_skip_frames"), FString::Printf(TEXT("%d"), framesToSkip)); SetTag(TEXT("sentry_unreal_exception_type"), type); SetTag(TEXT("sentry_unreal_exception_message"), message); PLATFORM_BREAK(); return nullptr; } TSharedPtr SentrySubsystemAndroid::CaptureEnsure(const FString& type, const FString& message) { auto id = FSentryJavaObjectWrapper::CallStaticObjectMethod(SentryJavaClasses::SentryBridgeJava, "captureException", "(Ljava/lang/String;Ljava/lang/String;)Lio/sentry/protocol/SentryId;", *FSentryJavaObjectWrapper::GetJString(type), *FSentryJavaObjectWrapper::GetJString(message)); return MakeShareable(new SentryIdAndroid(*id)); } void SentrySubsystemAndroid::CaptureUserFeedback(TSharedPtr userFeedback) { TSharedPtr userFeedbackAndroid = StaticCastSharedPtr(userFeedback); FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::Sentry, "captureUserFeedback", "(Lio/sentry/UserFeedback;)V", userFeedbackAndroid->GetJObject()); } void SentrySubsystemAndroid::SetUser(TSharedPtr user) { TSharedPtr userAndroid = StaticCastSharedPtr(user); FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::Sentry, "setUser", "(Lio/sentry/protocol/User;)V", userAndroid->GetJObject()); } void SentrySubsystemAndroid::RemoveUser() { FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::Sentry, "setUser", "(Lio/sentry/protocol/User;)V", nullptr); } void SentrySubsystemAndroid::ConfigureScope(const FSentryScopeDelegate& onConfigureScope) { int64 scopeCallbackId = SentryScopeCallbackAndroid::SaveDelegate(onConfigureScope); FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::SentryBridgeJava, "configureScope", "(J)V", scopeCallbackId); } void SentrySubsystemAndroid::SetContext(const FString& key, const TMap& values) { FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::SentryBridgeJava, "setContext", "(Ljava/lang/String;Ljava/util/HashMap;)V", *FSentryJavaObjectWrapper::GetJString(key), SentryConvertorsAndroid::StringMapToNative(values)->GetJObject()); } void SentrySubsystemAndroid::SetTag(const FString& key, const FString& value) { FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::SentryBridgeJava, "setTag", "(Ljava/lang/String;Ljava/lang/String;)V", *FSentryJavaObjectWrapper::GetJString(key), *FSentryJavaObjectWrapper::GetJString(value)); } void SentrySubsystemAndroid::RemoveTag(const FString& key) { FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::SentryBridgeJava, "removeTag", "(Ljava/lang/String;)V", *FSentryJavaObjectWrapper::GetJString(key)); } void SentrySubsystemAndroid::SetLevel(ESentryLevel level) { FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::SentryBridgeJava, "setLevel", "(Lio/sentry/SentryLevel;)V", SentryConvertorsAndroid::SentryLevelToNative(level)->GetJObject()); } void SentrySubsystemAndroid::StartSession() { FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::Sentry, "startSession", "()V", nullptr); } void SentrySubsystemAndroid::EndSession() { FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::Sentry, "endSession", "()V", nullptr); } TSharedPtr SentrySubsystemAndroid::StartTransaction(const FString& name, const FString& operation) { auto transaction = FSentryJavaObjectWrapper::CallStaticObjectMethod(SentryJavaClasses::Sentry, "startTransaction", "(Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ITransaction;", *FSentryJavaObjectWrapper::GetJString(name), *FSentryJavaObjectWrapper::GetJString(operation)); return MakeShareable(new SentryTransactionAndroid(*transaction)); } TSharedPtr SentrySubsystemAndroid::StartTransactionWithContext(TSharedPtr context) { TSharedPtr transactionContextAndroid = StaticCastSharedPtr(context); auto transaction = FSentryJavaObjectWrapper::CallStaticObjectMethod(SentryJavaClasses::Sentry, "startTransaction", "(Lio/sentry/TransactionContext;)Lio/sentry/ITransaction;", transactionContextAndroid->GetJObject()); return MakeShareable(new SentryTransactionAndroid(*transaction)); } TSharedPtr SentrySubsystemAndroid::StartTransactionWithContextAndTimestamp(TSharedPtr context, int64 timestamp) { UE_LOG(LogSentrySdk, Log, TEXT("Setting transaction timestamp explicitly not supported on Android.")); return StartTransactionWithContext(context); } TSharedPtr SentrySubsystemAndroid::StartTransactionWithContextAndOptions(TSharedPtr context, const TMap& options) { TSharedPtr transactionContextAndroid = StaticCastSharedPtr(context); TSharedPtr transactionOptionsAndroid = MakeShareable(new SentryTransactionOptionsAndroid()); transactionOptionsAndroid->SetCustomSamplingContext(options); auto transaction = FSentryJavaObjectWrapper::CallStaticObjectMethod(SentryJavaClasses::Sentry, "startTransaction", "(Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction;", transactionContextAndroid->GetJObject(), transactionOptionsAndroid->GetJObject()); return MakeShareable(new SentryTransactionAndroid(*transaction)); } TSharedPtr SentrySubsystemAndroid::ContinueTrace(const FString& sentryTrace, const TArray& baggageHeaders) { auto transactionContext = FSentryJavaObjectWrapper::CallStaticObjectMethod(SentryJavaClasses::Sentry, "continueTrace", "(Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext;", *FSentryJavaObjectWrapper::GetJString(sentryTrace), SentryConvertorsAndroid::StringArrayToNative(baggageHeaders)->GetJObject()); return MakeShareable(new SentryTransactionContextAndroid(*transactionContext)); }