diff --git a/.buildkite/pipeline.full.yml b/.buildkite/pipeline.full.yml index 9b45e5f1..de4def33 100644 --- a/.buildkite/pipeline.full.yml +++ b/.buildkite/pipeline.full.yml @@ -21,14 +21,14 @@ steps: upload: - "/Users/administrator/Library/Logs/Unreal Engine/LocalBuildLogs/*" artifact_paths: [Build/Plugin/*.zip] - timeout_in_minutes: 30 + timeout_in_minutes: 60 key: plugin_4_25 # UE 4.26 - label: 'Build Plugin - 4.26 Mac' env: UE_VERSION: "4.26" - DEVELOPER_DIR: "/Applications/Xcode13.app" + DEVELOPER_DIR: "/Applications/Xcode13.2.1.app" commands: - rm -rf "/Users/administrator/Library/Logs/Unreal Engine/LocalBuildLogs/*" - make package @@ -37,14 +37,14 @@ steps: upload: - "/Users/administrator/Library/Logs/Unreal Engine/LocalBuildLogs/*" artifact_paths: [Build/Plugin/*.zip] - timeout_in_minutes: 30 + timeout_in_minutes: 60 key: plugin_4_26 # UE 5.0 - label: 'Build Plugin - 5.0 Mac' env: UE_VERSION: "5.0" - DEVELOPER_DIR: "/Applications/Xcode13.app" + DEVELOPER_DIR: "/Applications/Xcode13.2.1.app" commands: - rm -rf "/Users/administrator/Library/Logs/Unreal Engine/LocalBuildLogs/*" - make package @@ -53,19 +53,19 @@ steps: upload: - "/Users/administrator/Library/Logs/Unreal Engine/LocalBuildLogs/*" artifact_paths: [Build/Plugin/*.zip] - timeout_in_minutes: 30 + timeout_in_minutes: 60 key: plugin_5_0 # # Build Test Fixtures # - # UE 5.0EA + # UE 5.0 - name: ':android: Build E2E - 5.0 Android' depends_on: plugin_5_0 env: UE_VERSION: "5.0" - DEVELOPER_DIR: "/Applications/Xcode13.app" + DEVELOPER_DIR: "/Applications/Xcode13.2.1.app" plugins: artifacts#v1.5.0: download: Build/Plugin/Bugsnag-*-UE_5.0-macOS.zip @@ -83,7 +83,7 @@ steps: depends_on: plugin_5_0 env: UE_VERSION: "5.0" - DEVELOPER_DIR: "/Applications/Xcode13.app" + DEVELOPER_DIR: "/Applications/Xcode13.2.1.app" plugins: artifacts#v1.5.0: download: Build/Plugin/Bugsnag-*-UE_5.0-macOS.zip @@ -101,7 +101,7 @@ steps: depends_on: plugin_5_0 env: UE_VERSION: "5.0" - DEVELOPER_DIR: "/Applications/Xcode13.app" + DEVELOPER_DIR: "/Applications/Xcode13.2.1.app" plugins: artifacts#v1.5.0: download: Build/Plugin/Bugsnag-*-UE_5.0-macOS.zip @@ -137,7 +137,7 @@ steps: - "--device=ANDROID_11_0" - "--farm=bs" - "--order=random" - concurrency: 9 + concurrency: 24 concurrency_group: browserstack-app concurrency_method: eager retry: @@ -164,7 +164,7 @@ steps: - "--device=ANDROID_11_0" - "--farm=bs" - "--order=random" - concurrency: 9 + concurrency: 24 concurrency_group: browserstack-app concurrency_method: eager retry: @@ -191,7 +191,7 @@ steps: - "--device=IOS_14" - "--farm=bs" - "--order=random" - concurrency: 9 + concurrency: 24 concurrency_group: browserstack-app concurrency_method: eager retry: diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index e5439f3e..c4f958b4 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -22,14 +22,14 @@ steps: upload: - "/Users/administrator/Library/Logs/Unreal Engine/LocalBuildLogs/*" artifact_paths: [Build/Plugin/*.zip] - timeout_in_minutes: 30 + timeout_in_minutes: 60 key: plugin_4_23 # UE 4.27 - label: 'Build Plugin - 4.27 Mac' env: UE_VERSION: "4.27" - DEVELOPER_DIR: "/Applications/Xcode13.app" + DEVELOPER_DIR: "/Applications/Xcode13.2.1.app" commands: - rm -rf "/Users/administrator/Library/Logs/Unreal Engine/LocalBuildLogs/*" - make package @@ -38,7 +38,7 @@ steps: upload: - "/Users/administrator/Library/Logs/Unreal Engine/LocalBuildLogs/*" artifact_paths: [Build/Plugin/*.zip] - timeout_in_minutes: 30 + timeout_in_minutes: 60 key: plugin_4_27 - label: 'Build Plugin - 4.27 Win' @@ -48,7 +48,7 @@ steps: UE_VERSION: "4.27" command: build.bat artifact_paths: [Bugsnag-*.zip] - timeout_in_minutes: 30 + timeout_in_minutes: 60 # # Build Test Fixtures @@ -59,7 +59,7 @@ steps: depends_on: plugin_4_27 env: UE_VERSION: "4.27" - DEVELOPER_DIR: "/Applications/Xcode13.app" + DEVELOPER_DIR: "/Applications/Xcode13.2.1.app" plugins: artifacts#v1.5.0: download: Build/Plugin/Bugsnag-*-UE_4.27-macOS.zip @@ -77,7 +77,7 @@ steps: depends_on: plugin_4_27 env: UE_VERSION: "4.27" - DEVELOPER_DIR: "/Applications/Xcode13.app" + DEVELOPER_DIR: "/Applications/Xcode13.2.1.app" plugins: artifacts#v1.5.0: download: Build/Plugin/Bugsnag-*-UE_4.27-macOS.zip @@ -95,7 +95,7 @@ steps: depends_on: plugin_4_27 env: UE_VERSION: "4.27" - DEVELOPER_DIR: "/Applications/Xcode13.app" + DEVELOPER_DIR: "/Applications/Xcode13.2.1.app" plugins: artifacts#v1.5.0: download: Build/Plugin/Bugsnag-*-UE_4.27-macOS.zip @@ -131,7 +131,7 @@ steps: - "--device=ANDROID_11_0" - "--farm=bs" - "--order=random" - concurrency: 9 + concurrency: 24 concurrency_group: browserstack-app concurrency_method: eager retry: @@ -158,7 +158,7 @@ steps: - "--device=IOS_12" - "--farm=bs" - "--order=random" - concurrency: 9 + concurrency: 24 concurrency_group: browserstack-app concurrency_method: eager retry: diff --git a/.cspell.json b/.cspell.json index 380ebafe..ff548713 100644 --- a/.cspell.json +++ b/.cspell.json @@ -6,12 +6,20 @@ "BSGJSON", "Bugsnag", "Expojs", + "FORCENOINLINE", "GRHI", + "Gameplay", "JNICALL", "JNIEXPORT", + "LOCTEXT", + "Landroid", "Lcom", "Ljava", + "Memzero", + "Millis", + "NSJSON", "Reactnativejs", + "Timespan", "UCLASS", "UENUM", "UFUNCTION", @@ -20,6 +28,7 @@ "checkf", "clazz", "cronut", + "jailbroken", "jboolean", "jbyte", "jclass", @@ -30,6 +39,7 @@ "jobject", "jsize", "ooms", + "symbolicate", "uproject", ], "ignorePaths": [ diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7b560822..7b1f640a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,4 +10,4 @@ jobs: - name: clang-format run: find Source Plugins/Bugsnag/Source/Bugsnag features/fixtures/generic/Source -name '*.h' -o -name '*.cpp' | xargs /usr/bin/clang-format-12 --dry-run --Werror - name: cspell - run: npm install -g cspell && cspell **/*.{cpp,h} + run: npm install -g cspell && cspell Plugins/Bugsnag/**/*.{cpp,h} diff --git a/CHANGELOG.md b/CHANGELOG.md index bcd7713f..8fac4556 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ Changelog ========= +## 1.5.0 (2022-07-06) + +* Adds the `Telemetry` configuration property. +* Updates the bugsnag-android dependency from v5.22.1 to [v5.24.0](/~https://github.com/bugsnag/bugsnag-android/blob/master/CHANGELOG.md#5240-2022-06-30) +* Updates the bugsnag-cocoa dependency from v6.16.8 to [v6.19.0](/~https://github.com/bugsnag/bugsnag-cocoa/blob/master/CHANGELOG.md#6190-2022-06-29) + ## 1.4.0 (2022-05-11) * Adds official support for Unreal Engine 5.0 🚀 diff --git a/Makefile b/Makefile index 0b2ba1db..6c6a93a0 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,7 @@ format: ## format all c/c++ source to match .clang-format .PHONY: lint lint: ## check the project for formatting or spelling issues find Source Plugins/Bugsnag/Source/Bugsnag features/fixtures/generic/Source -name '*.h' -o -name '*.cpp' | xargs clang-format --dry-run --Werror - cspell **/*.{cpp,h} + cspell Plugins/Bugsnag/**/*.{cpp,h} #------------------------------------------------------------------------------- # E2E test run diff --git a/Plugins/Bugsnag/Bugsnag.uplugin b/Plugins/Bugsnag/Bugsnag.uplugin index 02d1ce18..fe339a68 100644 --- a/Plugins/Bugsnag/Bugsnag.uplugin +++ b/Plugins/Bugsnag/Bugsnag.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, - "Version": 8, - "VersionName": "1.4.0", + "Version": 9, + "VersionName": "1.5.0", "FriendlyName": "Bugsnag", "Description": "Bugsnag is an error monitoring and application stability management solution.", "Category": "Developer", diff --git a/Plugins/Bugsnag/Source/Bugsnag/Bugsnag_UPL.xml b/Plugins/Bugsnag/Source/Bugsnag/Bugsnag_UPL.xml index de349266..604c04f0 100644 --- a/Plugins/Bugsnag/Source/Bugsnag/Bugsnag_UPL.xml +++ b/Plugins/Bugsnag/Source/Bugsnag/Bugsnag_UPL.xml @@ -73,8 +73,8 @@ - com.bugsnag,bugsnag-plugin-android-unreal,1.4.0 - com.bugsnag,bugsnag-android,5.22.1 + com.bugsnag,bugsnag-plugin-android-unreal,1.5.0 + com.bugsnag,bugsnag-android,5.24.0 diff --git a/Plugins/Bugsnag/Source/Bugsnag/Private/Android/AndroidPlatformConfiguration.cpp b/Plugins/Bugsnag/Source/Bugsnag/Private/Android/AndroidPlatformConfiguration.cpp index 5ff3581d..37720cf0 100644 --- a/Plugins/Bugsnag/Source/Bugsnag/Private/Android/AndroidPlatformConfiguration.cpp +++ b/Plugins/Bugsnag/Source/Bugsnag/Private/Android/AndroidPlatformConfiguration.cpp @@ -91,6 +91,11 @@ jobject FAndroidPlatformConfiguration::Parse(JNIEnv* Env, jniCallWithObjects(Env, jConfig, Cache->ConfigSetMaxPersistedSessions, Config->GetMaxPersistedSessions()); jniCallWithObjects(Env, jConfig, Cache->ConfigSetMaxReportedThreads, Config->GetMaxReportedThreads()); jniCallWithBool(Env, jConfig, Cache->ConfigSetPersistUser, Config->GetPersistUser()); + + jobject jTelemetry = FAndroidPlatformJNI::ParseTelemetryTypeSet(Env, Cache, Config->GetTelemetry()); + ReturnNullOnFail(jTelemetry); + jniCallWithObjects(Env, jConfig, Cache->ConfigSetTelemetry, jTelemetry); + if (Config->GetRedactedKeys().Num()) { jniCallWithSet(Env, Cache, jConfig, Cache->ConfigSetRedactedKeys, Config->GetRedactedKeys()); diff --git a/Plugins/Bugsnag/Source/Bugsnag/Private/Android/JNIUtilities.cpp b/Plugins/Bugsnag/Source/Bugsnag/Private/Android/JNIUtilities.cpp index 32ddc594..17dd2ef7 100644 --- a/Plugins/Bugsnag/Source/Bugsnag/Private/Android/JNIUtilities.cpp +++ b/Plugins/Bugsnag/Source/Bugsnag/Private/Android/JNIUtilities.cpp @@ -81,6 +81,7 @@ bool FAndroidPlatformJNI::LoadReferenceCache(JNIEnv* env, JNIReferenceCache* cac CacheExternalJavaClass(env, cache->SessionClass, "com.bugsnag.android.Session"); CacheExternalJavaClass(env, cache->SeverityClass, "com.bugsnag.android.Severity"); CacheExternalJavaClass(env, cache->StackframeClass, "com.bugsnag.android.Stackframe"); + CacheExternalJavaClass(env, cache->TelemetryClass, "com.bugsnag.android.Telemetry"); CacheExternalJavaClass(env, cache->ThreadClass, "com.bugsnag.android.Thread"); CacheExternalJavaClass(env, cache->UserClass, "com.bugsnag.android.User"); CacheExternalJavaClass(env, cache->EndpointConfigurationClass, "com.bugsnag.android.EndpointConfiguration"); @@ -200,6 +201,7 @@ bool FAndroidPlatformJNI::LoadReferenceCache(JNIEnv* env, JNIReferenceCache* cac CacheInstanceJavaMethod(env, cache->ConfigSetReleaseStage, cache->ConfigClass, "setReleaseStage", "(Ljava/lang/String;)V"); CacheInstanceJavaMethod(env, cache->ConfigSetSendLaunchCrashesSynchronously, cache->ConfigClass, "setSendLaunchCrashesSynchronously", "(Z)V"); CacheInstanceJavaMethod(env, cache->ConfigSetSendThreads, cache->ConfigClass, "setSendThreads", "(Lcom/bugsnag/android/ThreadSendPolicy;)V"); + CacheInstanceJavaMethod(env, cache->ConfigSetTelemetry, cache->ConfigClass, "setTelemetry", "(Ljava/util/Set;)V"); CacheInstanceJavaMethod(env, cache->ConfigSetUser, cache->ConfigClass, "setUser", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); CacheInstanceJavaMethod(env, cache->ConfigSetVersionCode, cache->ConfigClass, "setVersionCode", "(Ljava/lang/Integer;)V"); @@ -365,6 +367,7 @@ bool FAndroidPlatformJNI::LoadReferenceCache(JNIEnv* env, JNIReferenceCache* cac CacheStaticJavaField(env, cache->BreadcrumbTypeUser, cache->BreadcrumbTypeClass, "USER", "Lcom/bugsnag/android/BreadcrumbType;"); CacheStaticJavaField(env, cache->ErrorTypeAndroid, cache->ErrorTypeClass, "ANDROID", "Lcom/bugsnag/android/ErrorType;"); CacheStaticJavaField(env, cache->ErrorTypeC, cache->ErrorTypeClass, "C", "Lcom/bugsnag/android/ErrorType;"); + CacheStaticJavaField(env, cache->TelemetryInternalErrors, cache->TelemetryClass, "INTERNAL_ERRORS", "Lcom/bugsnag/android/Telemetry;"); CacheStaticJavaField(env, cache->ThreadSendPolicyAlways, cache->ThreadSendPolicyClass, "ALWAYS", "Lcom/bugsnag/android/ThreadSendPolicy;"); CacheStaticJavaField(env, cache->ThreadSendPolicyUnhandledOnly, cache->ThreadSendPolicyClass, "UNHANDLED_ONLY", "Lcom/bugsnag/android/ThreadSendPolicy;"); CacheStaticJavaField(env, cache->ThreadSendPolicyNever, cache->ThreadSendPolicyClass, "NEVER", "Lcom/bugsnag/android/ThreadSendPolicy;"); @@ -489,17 +492,18 @@ jobject FAndroidPlatformJNI::ParseJsonObject(JNIEnv* Env, const JNIReferenceCach * Adds a value to a set if enabled * * @param Env A JNI environment for the current thread + * @param Cache the JNI reference cache * @param ShouldAdd true if the value should be added * @param TypeClass the enum class * @param FieldName the name of the field to add to the set * * @return true if operation completed without error */ -static bool addTypeToSet(JNIEnv* Env, jobject jSet, bool ShouldAdd, const JNIReferenceCache* Cache, jfieldID FieldName) +static bool addTypeToSet(JNIEnv* Env, const JNIReferenceCache* Cache, jobject jSet, bool ShouldAdd, jclass TypeClass, jfieldID FieldName) { if (ShouldAdd) { - jobject jType = (*Env).GetStaticObjectField(Cache->BreadcrumbTypeClass, FieldName); + jobject jType = (*Env).GetStaticObjectField(TypeClass, FieldName); if (FAndroidPlatformJNI::CheckAndClearException(Env)) { return false; @@ -551,13 +555,29 @@ jobject FAndroidPlatformJNI::ParseBreadcrumbTypeSet(JNIEnv* Env, const JNIRefere { return nullptr; } - if (addTypeToSet(Env, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::Error), Cache, Cache->BreadcrumbTypeError) && - addTypeToSet(Env, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::Log), Cache, Cache->BreadcrumbTypeLog) && - addTypeToSet(Env, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::Navigation), Cache, Cache->BreadcrumbTypeNavigation) && - addTypeToSet(Env, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::Process), Cache, Cache->BreadcrumbTypeProcess) && - addTypeToSet(Env, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::Request), Cache, Cache->BreadcrumbTypeRequest) && - addTypeToSet(Env, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::State), Cache, Cache->BreadcrumbTypeState) && - addTypeToSet(Env, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::User), Cache, Cache->BreadcrumbTypeUser)) + jclass TypeClass = Cache->BreadcrumbTypeClass; + if (addTypeToSet(Env, Cache, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::Error), TypeClass, Cache->BreadcrumbTypeError) && + addTypeToSet(Env, Cache, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::Log), TypeClass, Cache->BreadcrumbTypeLog) && + addTypeToSet(Env, Cache, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::Navigation), TypeClass, Cache->BreadcrumbTypeNavigation) && + addTypeToSet(Env, Cache, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::Process), TypeClass, Cache->BreadcrumbTypeProcess) && + addTypeToSet(Env, Cache, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::Request), TypeClass, Cache->BreadcrumbTypeRequest) && + addTypeToSet(Env, Cache, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::State), TypeClass, Cache->BreadcrumbTypeState) && + addTypeToSet(Env, Cache, jSet, EnumHasAllFlags(Value, EBugsnagEnabledBreadcrumbTypes::User), TypeClass, Cache->BreadcrumbTypeUser)) + { + return jSet; + } + return nullptr; +} + +jobject FAndroidPlatformJNI::ParseTelemetryTypeSet(JNIEnv* Env, const JNIReferenceCache* Cache, const EBugsnagTelemetryTypes Value) +{ + jobject jSet = Env->NewObject(Cache->HashSetClass, Cache->HashSetConstructor); + if (FAndroidPlatformJNI::CheckAndClearException(Env)) + { + return nullptr; + } + jclass TypeClass = Cache->TelemetryClass; + if (addTypeToSet(Env, Cache, jSet, EnumHasAllFlags(Value, EBugsnagTelemetryTypes::InternalErrors), TypeClass, Cache->TelemetryInternalErrors)) { return jSet; } diff --git a/Plugins/Bugsnag/Source/Bugsnag/Private/Android/JNIUtilities.h b/Plugins/Bugsnag/Source/Bugsnag/Private/Android/JNIUtilities.h index 10200ea5..d909dfec 100644 --- a/Plugins/Bugsnag/Source/Bugsnag/Private/Android/JNIUtilities.h +++ b/Plugins/Bugsnag/Source/Bugsnag/Private/Android/JNIUtilities.h @@ -7,6 +7,7 @@ #include "BugsnagBreadcrumb.h" #include "BugsnagEvent.h" #include "BugsnagSettings.h" +#include "BugsnagTelemetryTypes.h" typedef struct { @@ -31,6 +32,7 @@ typedef struct jclass MetadataParserClass; jclass MetadataSerializerClass; jclass NotifierClass; + jclass TelemetryClass; jclass ThreadClass; jclass ThreadSendPolicyClass; jclass ThreadTypeClass; @@ -141,6 +143,7 @@ typedef struct jmethodID ConfigSetReleaseStage; jmethodID ConfigSetSendLaunchCrashesSynchronously; jmethodID ConfigSetSendThreads; + jmethodID ConfigSetTelemetry; jmethodID ConfigSetUser; jmethodID ConfigSetVersionCode; jmethodID ContextGetApplication; @@ -283,6 +286,7 @@ typedef struct jfieldID BreadcrumbTypeUser; jfieldID ErrorTypeAndroid; jfieldID ErrorTypeC; + jfieldID TelemetryInternalErrors; jfieldID ThreadSendPolicyAlways; jfieldID ThreadSendPolicyUnhandledOnly; jfieldID ThreadSendPolicyNever; @@ -368,6 +372,8 @@ class FAndroidPlatformJNI */ static jobject ParseBreadcrumbTypeSet(JNIEnv* Env, const JNIReferenceCache* Cache, const EBugsnagEnabledBreadcrumbTypes Value); + static jobject ParseTelemetryTypeSet(JNIEnv* Env, const JNIReferenceCache* Cache, const EBugsnagTelemetryTypes Value); + /** * Convert a value into a Java Severity * diff --git a/Plugins/Bugsnag/Source/Bugsnag/Private/Android/Specs/AndroidPlatformConfiguration.spec.cpp b/Plugins/Bugsnag/Source/Bugsnag/Private/Android/Specs/AndroidPlatformConfiguration.spec.cpp new file mode 100644 index 00000000..25ea2ec8 --- /dev/null +++ b/Plugins/Bugsnag/Source/Bugsnag/Private/Android/Specs/AndroidPlatformConfiguration.spec.cpp @@ -0,0 +1,65 @@ +// Copyright 2022 Bugsnag. All Rights Reserved. + +#include "Android/AndroidJavaEnv.h" +#include "AutomationTest.h" + +#include "../AndroidPlatformConfiguration.h" + +// +// To run the unit tests on Android: +// * Build & run the example app on a connected Android device. +// * Open Unreal Editor's "Session Frontend" and find the running game in "My Sessions" +// * Click the "Automation" tab, select the tests to run, and click "Start Tests"! +// +BEGIN_DEFINE_SPEC(FAndroidPlatformConfigurationSpec, "Bugsnag.FAndroidPlatformConfigurationSpec", + EAutomationTestFlags::ProductFilter | EAutomationTestFlags::ApplicationContextMask) +END_DEFINE_SPEC(FAndroidPlatformConfigurationSpec) +void FAndroidPlatformConfigurationSpec::Define() +{ + static const FString ApiKey = TEXT("0192837465afbecd0192837465afbecd"); + + static JNIReferenceCache JNICache; + if (!JNICache.loaded) + { + JNICache.loaded = FAndroidPlatformJNI::LoadReferenceCache(AndroidJavaEnv::GetJavaEnv(), &JNICache); + } + + Describe("Telemetry", [this]() + { + It("Should contain all types by default", [this]() + { + JNIEnv* Env = AndroidJavaEnv::GetJavaEnv(); + jmethodID SizeMethod = Env->GetMethodID(Env->FindClass("java/util/Set"), "size", "()I"); + jmethodID GetTelemetryMethod = Env->GetMethodID(JNICache.ConfigClass, "getTelemetry", "()Ljava/util/Set;"); + TEST_FALSE(Env->ExceptionCheck()); + + TSharedRef Configuration = MakeShared(ApiKey); + + jobject AndroidConfig = FAndroidPlatformConfiguration::Parse(Env, &JNICache, Configuration); + jobject AndroidTelemetry = Env->CallObjectMethod(AndroidConfig, GetTelemetryMethod); + TEST_TRUE(AndroidTelemetry != nullptr); + TEST_FALSE(Env->ExceptionCheck()); + int Size = Env->CallIntMethod(AndroidTelemetry, SizeMethod); + TEST_FALSE(Env->ExceptionCheck()); + TestEqual(TEXT("Telemetry Set should contain [INTERNAL_ERRORS]"), Size, 1); + }); + + It("Should be empty after setting EBugsnagTelemetryTypes::None", [this]() + { + JNIEnv* Env = AndroidJavaEnv::GetJavaEnv(); + jmethodID SizeMethod = Env->GetMethodID(Env->FindClass("java/util/Set"), "size", "()I"); + jmethodID GetTelemetryMethod = Env->GetMethodID(JNICache.ConfigClass, "getTelemetry", "()Ljava/util/Set;"); + + TSharedRef Configuration = MakeShared(ApiKey); + Configuration->SetTelemetry(EBugsnagTelemetryTypes::None); + + jobject AndroidConfig = FAndroidPlatformConfiguration::Parse(Env, &JNICache, Configuration); + jobject AndroidTelemetry = Env->CallObjectMethod(AndroidConfig, GetTelemetryMethod); + TEST_TRUE(AndroidTelemetry != nullptr); + TEST_FALSE(Env->ExceptionCheck()); + int Size = Env->CallIntMethod(AndroidTelemetry, SizeMethod); + TEST_FALSE(Env->ExceptionCheck()); + TestEqual(TEXT("Telemetry Set should be empty"), Size, 0); + }); + }); +} diff --git a/Plugins/Bugsnag/Source/Bugsnag/Private/Android/Specs/AutomationTest.h b/Plugins/Bugsnag/Source/Bugsnag/Private/Android/Specs/AutomationTest.h new file mode 100644 index 00000000..d851d5cc --- /dev/null +++ b/Plugins/Bugsnag/Source/Bugsnag/Private/Android/Specs/AutomationTest.h @@ -0,0 +1,12 @@ +// Copyright 2022 Bugsnag. All Rights Reserved. + +#include "Misc/AutomationTest.h" + +#define TEST_TRUE(expression) \ + TestTrue(TEXT(#expression), expression) + +#define TEST_FALSE(expression) \ + TestFalse(TEXT(#expression), expression) + +#define TEST_EQUAL(expression, expected) \ + TestEqual(TEXT(#expression), expression, expected) diff --git a/Plugins/Bugsnag/Source/Bugsnag/Private/Apple/AppleBugsnagUtils.h b/Plugins/Bugsnag/Source/Bugsnag/Private/Apple/AppleBugsnagUtils.h index 646ed275..70a640ad 100644 --- a/Plugins/Bugsnag/Source/Bugsnag/Private/Apple/AppleBugsnagUtils.h +++ b/Plugins/Bugsnag/Source/Bugsnag/Private/Apple/AppleBugsnagUtils.h @@ -8,8 +8,6 @@ #include "Serialization/LargeMemoryReader.h" #include "Serialization/LargeMemoryWriter.h" -#import - // String conversion static inline FString FStringFromNSString(NSString* _Nullable String) @@ -61,7 +59,11 @@ static inline NSNumber* NSNumberFromOptional(const TOptional& Value) static inline TSharedPtr FJsonObjectFromNSDictionary(NSDictionary* Dictionary, NSError** Error = nil) { - NSData* Data = [BSGJSONSerialization dataWithJSONObject:Dictionary options:0 error:Error]; + if (!Dictionary || ![NSJSONSerialization isValidJSONObject:Dictionary]) + { + return nullptr; + } + NSData* Data = [NSJSONSerialization dataWithJSONObject:Dictionary options:0 error:Error]; if (Data) { TSharedPtr JsonObject; @@ -86,7 +88,7 @@ static inline NSDictionary* _Nullable NSDictionaryFromFJsonObject(const TSharedP { JsonWriter->Close(); NSData* Data = [NSData dataWithBytesNoCopy:Archive.GetData() length:Archive.TotalSize() freeWhenDone:NO]; - return [BSGJSONSerialization JSONObjectWithData:Data options:0 error:Error]; + return [NSJSONSerialization JSONObjectWithData:Data options:0 error:Error]; } return nil; } diff --git a/Plugins/Bugsnag/Source/Bugsnag/Private/Apple/ApplePlatformConfiguration.cpp b/Plugins/Bugsnag/Source/Bugsnag/Private/Apple/ApplePlatformConfiguration.cpp index 62091dae..815cdda9 100644 --- a/Plugins/Bugsnag/Source/Bugsnag/Private/Apple/ApplePlatformConfiguration.cpp +++ b/Plugins/Bugsnag/Source/Bugsnag/Private/Apple/ApplePlatformConfiguration.cpp @@ -12,10 +12,14 @@ #import #import -#import #import #import +static BSGTelemetryOptions GetTelemetryTypes(EBugsnagTelemetryTypes Value) +{ + return (EnumHasAllFlags(Value, EBugsnagTelemetryTypes::InternalErrors) ? BSGTelemetryInternalErrors : 0); +} + static BSGThreadSendPolicy GetThreadSendPolicy(EBugsnagSendThreadsPolicy Policy) { switch (Policy) @@ -111,6 +115,8 @@ BugsnagConfiguration* FApplePlatformConfiguration::Configuration(const TSharedRe CocoaConfig.persistUser = Configuration->GetPersistUser(); + CocoaConfig.telemetry = GetTelemetryTypes(Configuration->GetTelemetry()); + if (Configuration->GetReleaseStage().IsSet()) { CocoaConfig.releaseStage = NSStringFromFString(Configuration->GetReleaseStage().GetValue()); diff --git a/Plugins/Bugsnag/Source/Bugsnag/Private/Apple/Specs/ApplePlatformConfiguration.spec.cpp b/Plugins/Bugsnag/Source/Bugsnag/Private/Apple/Specs/ApplePlatformConfiguration.spec.cpp index 83a62776..05e5b29b 100644 --- a/Plugins/Bugsnag/Source/Bugsnag/Private/Apple/Specs/ApplePlatformConfiguration.spec.cpp +++ b/Plugins/Bugsnag/Source/Bugsnag/Private/Apple/Specs/ApplePlatformConfiguration.spec.cpp @@ -46,7 +46,6 @@ void FApplePlatformConfigurationSpec::Define() TEST_TRUE([CocoaConfig.appType isEqual:DefaultConfig.appType]); TEST_TRUE([CocoaConfig.bundleVersion isEqual:DefaultConfig.bundleVersion]); TEST_TRUE([CocoaConfig.redactedKeys isEqual:DefaultConfig.redactedKeys]); - TEST_TRUE([CocoaConfig.releaseStage isEqual:DefaultConfig.releaseStage]); }); It("ApiKey", [this]() @@ -185,6 +184,16 @@ void FApplePlatformConfigurationSpec::Define() TEST_EQUAL(CocoaConfig.persistUser, NO); }); + It("Telemetry", [this]() + { + TSharedRef Configuration = MakeShared(ApiKey); + BugsnagConfiguration* CocoaConfig = FApplePlatformConfiguration::Configuration(Configuration); + TEST_TRUE(CocoaConfig.telemetry == BSGTelemetryInternalErrors); + Configuration->SetTelemetry(EBugsnagTelemetryTypes::None); + CocoaConfig = FApplePlatformConfiguration::Configuration(Configuration); + TEST_TRUE(CocoaConfig.telemetry == 0); + }); + It("ReleaseStage", [this]() { TSharedRef Configuration = MakeShared(ApiKey); diff --git a/Plugins/Bugsnag/Source/Bugsnag/Private/BugsnagConfiguration.cpp b/Plugins/Bugsnag/Source/Bugsnag/Private/BugsnagConfiguration.cpp index ac520c8a..0ba9ee93 100644 --- a/Plugins/Bugsnag/Source/Bugsnag/Private/BugsnagConfiguration.cpp +++ b/Plugins/Bugsnag/Source/Bugsnag/Private/BugsnagConfiguration.cpp @@ -33,6 +33,11 @@ static EBugsnagEnabledBreadcrumbTypes Convert(FBugsnagEnabledBreadcrumbTypes Val (Value.bUser ? EBugsnagEnabledBreadcrumbTypes::User : EBugsnagEnabledBreadcrumbTypes::None); } +static EBugsnagTelemetryTypes Convert(const FBugsnagTelemetryTypes& Value) +{ + return (Value.bInternalErrors ? EBugsnagTelemetryTypes::InternalErrors : EBugsnagTelemetryTypes::None); +} + uint64 const FBugsnagConfiguration::AppHangThresholdFatalOnly = INT_MAX; FBugsnagConfiguration::FBugsnagConfiguration(const FString& ApiKey) @@ -56,6 +61,7 @@ FBugsnagConfiguration::FBugsnagConfiguration(const UBugsnagSettings& Settings) , MaxPersistedEvents(Settings.MaxPersistedEvents) , MaxPersistedSessions(Settings.MaxPersistedSessions) , bPersistUser(Settings.bPersistUser) + , Telemetry(Convert(Settings.Telemetry)) , ReleaseStage(UnsetIfEmpty(Settings.ReleaseStage)) , AppType(UnsetIfEmpty(Settings.AppType)) , AppVersion(UnsetIfEmpty(Settings.AppVersion)) diff --git a/Plugins/Bugsnag/Source/Bugsnag/Private/Version.h b/Plugins/Bugsnag/Source/Bugsnag/Private/Version.h index 4ea30b1c..6dde5ae2 100644 --- a/Plugins/Bugsnag/Source/Bugsnag/Private/Version.h +++ b/Plugins/Bugsnag/Source/Bugsnag/Private/Version.h @@ -4,4 +4,4 @@ #define BUGSNAG_UNREAL_NAME "Unreal Bugsnag Notifier" #define BUGSNAG_UNREAL_URL "/~https://github.com/bugsnag/bugsnag-unreal" -#define BUGSNAG_UNREAL_VERSION_STRING "1.4.0" +#define BUGSNAG_UNREAL_VERSION_STRING "1.5.0" diff --git a/Plugins/Bugsnag/Source/Bugsnag/Public/BugsnagConfiguration.h b/Plugins/Bugsnag/Source/Bugsnag/Public/BugsnagConfiguration.h index 0f7a24ab..75545c6b 100644 --- a/Plugins/Bugsnag/Source/Bugsnag/Public/BugsnagConfiguration.h +++ b/Plugins/Bugsnag/Source/Bugsnag/Public/BugsnagConfiguration.h @@ -9,6 +9,7 @@ #include "BugsnagMetadataStore.h" #include "BugsnagSession.h" #include "BugsnagSettings.h" +#include "BugsnagTelemetryTypes.h" #include "BugsnagUser.h" #include "Dom/JsonObject.h" @@ -298,6 +299,18 @@ class BUGSNAG_API FBugsnagConfiguration final : public IBugsnagFeatureFlagStore, */ void SetPersistUser(bool Value) { bPersistUser = Value; } + /** + * The types of telemetry that may be sent to Bugsnag for product improvement purposes. + * + * By default all types of telemetry are enabled. + */ + EBugsnagTelemetryTypes GetTelemetry() const { return Telemetry; } + + /** + * @param Value The types of telemetry that may be sent to Bugsnag for product improvement purposes. + */ + void SetTelemetry(EBugsnagTelemetryTypes Value) { Telemetry = Value; } + /** * The release stage of the application, such as `"production"`, `"development"`, `"beta"` etc. */ @@ -573,6 +586,7 @@ class BUGSNAG_API FBugsnagConfiguration final : public IBugsnagFeatureFlagStore, uint32 MaxPersistedSessions = 128; uint32 MaxReportedThreads = 200; bool bPersistUser = true; + EBugsnagTelemetryTypes Telemetry = EBugsnagTelemetryTypes::All; FBugsnagUser User; TOptional ReleaseStage; TOptional AppType; diff --git a/Plugins/Bugsnag/Source/Bugsnag/Public/BugsnagSettings.h b/Plugins/Bugsnag/Source/Bugsnag/Public/BugsnagSettings.h index 14fcba1e..685a7d2f 100644 --- a/Plugins/Bugsnag/Source/Bugsnag/Public/BugsnagSettings.h +++ b/Plugins/Bugsnag/Source/Bugsnag/Public/BugsnagSettings.h @@ -68,6 +68,19 @@ struct FBugsnagErrorTypes bool bThermalKills = true; }; +/** + * Types of telemetry that may be sent to Bugsnag for product improvement purposes. + */ +USTRUCT() +struct FBugsnagTelemetryTypes +{ + GENERATED_BODY() + + // Errors within the Bugsnag SDK. + UPROPERTY(GlobalConfig, EditAnywhere, Category = "Advanced Configuration") + bool bInternalErrors = true; +}; + /** * Controls whether Bugsnag events should include the state of all threads at the time of an error. */ @@ -133,7 +146,7 @@ class BUGSNAG_API UBugsnagSettings : public UObject UPROPERTY(GlobalConfig, EditAnywhere, Category = "Advanced Configuration", DisplayName = "Auto Upload Symbol Files (Android only)") bool bAutoUploadSymbolFiles = true; - // A general summary of what was occuring in the application. + // A general summary of what was occurring in the application. UPROPERTY(GlobalConfig, EditAnywhere, Category = "Advanced Configuration") FString Context; @@ -186,6 +199,10 @@ class BUGSNAG_API UBugsnagSettings : public UObject UPROPERTY(GlobalConfig, EditAnywhere, Category = "Advanced Configuration") bool bPersistUser = true; + // The types of telemetry that may be sent to Bugsnag for product improvement purposes. + UPROPERTY(GlobalConfig, EditAnywhere, Category = "Advanced Configuration") + FBugsnagTelemetryTypes Telemetry; + /////////////////////////////////////////////////////////////////////////// // // App Information diff --git a/Plugins/Bugsnag/Source/Bugsnag/Public/BugsnagTelemetryTypes.h b/Plugins/Bugsnag/Source/Bugsnag/Public/BugsnagTelemetryTypes.h new file mode 100644 index 00000000..5243e090 --- /dev/null +++ b/Plugins/Bugsnag/Source/Bugsnag/Public/BugsnagTelemetryTypes.h @@ -0,0 +1,16 @@ +// Copyright 2022 Bugsnag. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" + +/** + * Types of telemetry that may be sent to Bugsnag for product improvement purposes. + */ +enum class EBugsnagTelemetryTypes : uint8 +{ + None = 0, + InternalErrors = 1 << 0, + All = InternalErrors +}; +ENUM_CLASS_FLAGS(EBugsnagTelemetryTypes) diff --git a/VERSION b/VERSION index 88c5fb89..bc80560f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.0 +1.5.0 diff --git a/deps/bugsnag-cocoa b/deps/bugsnag-cocoa index 450d2172..5aeb6387 160000 --- a/deps/bugsnag-cocoa +++ b/deps/bugsnag-cocoa @@ -1 +1 @@ -Subproject commit 450d21727f99727d42d5d484b5c07df16c119b16 +Subproject commit 5aeb63871cc68d6a2533fef203859de0d021dca9 diff --git a/deps/bugsnag-plugin-android-unreal/build.gradle b/deps/bugsnag-plugin-android-unreal/build.gradle index d7177ace..1e97cf75 100644 --- a/deps/bugsnag-plugin-android-unreal/build.gradle +++ b/deps/bugsnag-plugin-android-unreal/build.gradle @@ -28,7 +28,7 @@ android { } dependencies { - api "com.bugsnag:bugsnag-android-core:5.22.1" + api "com.bugsnag:bugsnag-android-core:5.24.0" androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'junit:junit:4.12' @@ -40,7 +40,7 @@ task generatePom { pom { project { groupId 'com.bugsnag' - version '1.4.0' + version '1.5.0' packaging 'aar' } }.writeTo("$buildDir/outputs/aar/bugsnag-plugin-android-unreal-release.pom") diff --git a/deps/bugsnag-plugin-android-unreal/src/main/java/com/bugsnag/android/unreal/UnrealPlugin.java b/deps/bugsnag-plugin-android-unreal/src/main/java/com/bugsnag/android/unreal/UnrealPlugin.java index 0f19bc34..ddd3cd13 100644 --- a/deps/bugsnag-plugin-android-unreal/src/main/java/com/bugsnag/android/unreal/UnrealPlugin.java +++ b/deps/bugsnag-plugin-android-unreal/src/main/java/com/bugsnag/android/unreal/UnrealPlugin.java @@ -1,26 +1,24 @@ package com.bugsnag.android.unreal; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.bugsnag.android.Bugsnag; +import com.bugsnag.android.Breadcrumb; import com.bugsnag.android.Client; import com.bugsnag.android.Configuration; -import com.bugsnag.android.Breadcrumb; import com.bugsnag.android.Error; import com.bugsnag.android.ErrorType; import com.bugsnag.android.Event; import com.bugsnag.android.OnBreadcrumbCallback; import com.bugsnag.android.OnErrorCallback; -import com.bugsnag.android.OnSessionCallback; import com.bugsnag.android.OnSendCallback; +import com.bugsnag.android.OnSessionCallback; import com.bugsnag.android.Plugin; import com.bugsnag.android.Session; -import com.bugsnag.android.Severity; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; public class UnrealPlugin implements Plugin { static final String DEFAULT_HANDLED_REASON = "handledError"; @@ -37,16 +35,6 @@ public boolean onBreadcrumb(Breadcrumb crumb) { OnSendCallback onSendRunner = new OnSendCallback() { @Override public boolean onSend(Event event) { - // discard if the error class is in (the currently configured) discardClasses - if (discardClasses != null && discardClasses.size() > 0) { - for (Error err : event.getErrors()) { - String errorClass = err.getErrorClass(); - if (errorClass != null && discardClasses.contains(errorClass)) { - return false; - } - } - } - // if for some reason the plugin is unloaded when a callback is invoked, skip processing return loaded ? runEventCallbacks(event) : true; } @@ -102,7 +90,7 @@ public boolean onSession(Session session) { */ static native void setSeverityReason(Event event, String reasonType); - private Set discardClasses; + static private Set discardClasses; public UnrealPlugin(Configuration config) { config.addOnBreadcrumb(onBreadcrumbRunner); @@ -130,6 +118,10 @@ static void notify(String name, String message, StackTraceElement[] stacktrace, if (client == null || name == null) { return; } + if (discardClasses != null && discardClasses.contains(name)) { + return; + } + Throwable exc = new RuntimeException(); exc.setStackTrace(stacktrace);