diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fcd965d45..6f89d2c3c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ * Added `NativeOutOfMemoryPlugin` as a new way to report `OutOfMemoryError`s that uses pre-allocated memory in the NDK module instead of allocating an `Event` object. When used `OutOfMemoryError`s will be more reliably reported, but will not be passed to `OnErrorCallback`s (`OnSendCallback` works as expected). [#2384](https://github.com/bugsnag/bugsnag-android/pull/2384) * Added `appHangCooldownMillis` to the AppHangPlugin to control the number of AppHang errors produced when the app is performance constrained - []() + [#2389](https://github.com/bugsnag/bugsnag-android/pull/2389) +* Moved root/jailbreak detection onto a background thread so that it no longer blocks startup (this should improve startup performance in most common cases) + [#2391](https://github.com/bugsnag/bugsnag-android/pull/2391) ## 6.24.0 (2026-02-11) diff --git a/bugsnag-android-core/api/bugsnag-android-core.api b/bugsnag-android-core/api/bugsnag-android-core.api index ec8014b094..6bfa024573 100644 --- a/bugsnag-android-core/api/bugsnag-android-core.api +++ b/bugsnag-android-core/api/bugsnag-android-core.api @@ -877,6 +877,10 @@ public final class com/bugsnag/android/StateEvent$StartSession : com/bugsnag/and public final fun getUnhandledCount ()I } +public final class com/bugsnag/android/StateEvent$SynchronizeState : com/bugsnag/android/StateEvent { + public static final field INSTANCE Lcom/bugsnag/android/StateEvent$SynchronizeState; +} + public final class com/bugsnag/android/StateEvent$UpdateContext : com/bugsnag/android/StateEvent { public final field context Ljava/lang/String; public fun (Ljava/lang/String;)V @@ -1179,6 +1183,7 @@ public final class com/bugsnag/android/internal/TaskType : java/lang/Enum { public abstract interface class com/bugsnag/android/internal/dag/Provider { public abstract fun get ()Ljava/lang/Object; public abstract fun getOrNull ()Ljava/lang/Object; + public abstract fun isComplete ()Z } public abstract class com/bugsnag/android/internal/dag/RunnableProvider : com/bugsnag/android/internal/dag/Provider, java/lang/Runnable { @@ -1186,6 +1191,7 @@ public abstract class com/bugsnag/android/internal/dag/RunnableProvider : com/bu public fun get ()Ljava/lang/Object; public fun getOrNull ()Ljava/lang/Object; public abstract fun invoke ()Ljava/lang/Object; + public fun isComplete ()Z public final fun run ()V } @@ -1197,6 +1203,7 @@ public final class com/bugsnag/android/internal/dag/ValueProvider : com/bugsnag/ public fun get ()Ljava/lang/Object; public fun getOrNull ()Ljava/lang/Object; public fun hashCode ()I + public fun isComplete ()Z public fun toString ()Ljava/lang/String; } diff --git a/bugsnag-android-core/detekt-baseline.xml b/bugsnag-android-core/detekt-baseline.xml index 398e5c8809..f1bbc8b837 100644 --- a/bugsnag-android-core/detekt-baseline.xml +++ b/bugsnag-android-core/detekt-baseline.xml @@ -15,7 +15,7 @@ LongParameterList:AppWithState.kt$AppWithState$( binaryArch: String?, id: String?, releaseStage: String?, version: String?, codeBundleId: String?, buildUuid: Provider<String?>?, type: String?, versionCode: Number?, /** * The number of milliseconds the application was running before the event occurred */ var duration: Number?, /** * The number of milliseconds the application was running in the foreground before the * event occurred */ var durationInForeground: Number?, /** * Whether the application was in the foreground when the event occurred */ var inForeground: Boolean?, /** * Whether the application was launching when the event occurred */ var isLaunching: Boolean? ) LongParameterList:AppWithState.kt$AppWithState$( binaryArch: String?, id: String?, releaseStage: String?, version: String?, codeBundleId: String?, buildUuid: String?, type: String?, versionCode: Number?, /** * The number of milliseconds the application was running before the event occurred */ duration: Number?, /** * The number of milliseconds the application was running in the foreground before the * event occurred */ durationInForeground: Number?, /** * Whether the application was in the foreground when the event occurred */ inForeground: Boolean?, /** * Whether the application was launching when the event occurred */ isLaunching: Boolean? ) LongParameterList:AppWithState.kt$AppWithState$( config: ImmutableConfig, binaryArch: String?, id: String?, releaseStage: String?, version: String?, codeBundleId: String?, duration: Number?, durationInForeground: Number?, inForeground: Boolean?, isLaunching: Boolean? ) - LongParameterList:DataCollectionModule.kt$DataCollectionModule$( contextModule: ContextModule, configModule: ConfigModule, systemServiceModule: SystemServiceModule, trackerModule: TrackerModule, bgTaskService: BackgroundTaskService, connectivity: Connectivity, deviceIdStore: Provider<DeviceIdStore>, memoryTrimState: MemoryTrimState ) + LongParameterList:DataCollectionModule.kt$DataCollectionModule$( contextModule: ContextModule, configModule: ConfigModule, systemServiceModule: SystemServiceModule, trackerModule: TrackerModule, bgTaskService: BackgroundTaskService, connectivity: Connectivity, deviceId: Provider<DeviceIdStore.DeviceIds?>, memoryTrimState: MemoryTrimState, clientObservable: ClientObservable ) LongParameterList:Device.kt$Device$( buildInfo: DeviceBuildInfo, /** * The Application Binary Interface used */ var cpuAbi: Array<String>?, /** * Whether the device has been jailbroken */ var jailbroken: Boolean?, /** * A UUID generated by Bugsnag and used for the individual application on a device */ var id: String?, /** * The IETF language tag of the locale used */ var locale: String?, /** * The total number of bytes of memory on the device */ var totalMemory: Long?, /** * A collection of names and their versions of the primary languages, frameworks or * runtimes that the application is running on */ runtimeVersions: MutableMap<String, Any>? ) LongParameterList:DeviceBuildInfo.kt$DeviceBuildInfo$( val manufacturer: String?, val model: String?, val osVersion: String?, val apiLevel: Int?, val osBuild: String?, val fingerprint: String?, val tags: String?, val brand: String?, val cpuAbis: Array<String>? ) LongParameterList:DeviceDataCollector.kt$DeviceDataCollector$( private val connectivity: Connectivity, private val appContext: Context, resources: Resources, private val deviceIdStore: Provider<DeviceIdStore.DeviceIds?>, private val buildInfo: DeviceBuildInfo, private val dataDirectory: File, private val rootedFuture: Provider<Boolean>?, private val bgTaskService: BackgroundTaskService, private val logger: Logger ) @@ -84,7 +84,6 @@ SwallowedException:ConnectivityCompat.kt$ConnectivityLegacy$e: NullPointerException SwallowedException:ContextExtensions.kt$exc: RuntimeException SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$e: Throwable - SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$exc: Exception SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$exception: Exception SwallowedException:DeviceIdFilePersistence.kt$DeviceIdFilePersistence$exc: OverlappingFileLockException SwallowedException:EventStore.kt$EventStore$exception: RejectedExecutionException diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/Client.java b/bugsnag-android-core/src/main/java/com/bugsnag/android/Client.java index 38da469ca9..a248fc1735 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/Client.java +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/Client.java @@ -128,7 +128,7 @@ public Client(@NonNull Context androidContext, @NonNull String apiKey) { * @param configuration a configuration for the Client */ public Client(@NonNull Context androidContext, @NonNull final Configuration configuration) { - ContextModule contextModule = new ContextModule(androidContext, bgTaskService); + ContextModule contextModule = new ContextModule(androidContext); appContext = contextModule.getCtx(); notifier = configuration.getNotifier(); @@ -186,7 +186,7 @@ public Unit invoke(Boolean hasConnection, String networkState) { // lookup system services final SystemServiceModule systemServiceModule = - new SystemServiceModule(contextModule, bgTaskService); + new SystemServiceModule(contextModule); // setup further state trackers and data collection TrackerModule trackerModule = new TrackerModule(configModule, @@ -194,8 +194,8 @@ public Unit invoke(Boolean hasConnection, String networkState) { DataCollectionModule dataCollectionModule = new DataCollectionModule(contextModule, configModule, systemServiceModule, trackerModule, - bgTaskService, connectivity, storageModule.getDeviceIdStore(), - memoryTrimState); + bgTaskService, connectivity, storageModule.getDeviceId(), + memoryTrimState, clientObservable); // load the device + user information userState = storageModule.loadUser(configuration.getUser()); diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/ClientObservable.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/ClientObservable.kt index 989ea30fba..4811f29578 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/ClientObservable.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/ClientObservable.kt @@ -31,4 +31,8 @@ internal class ClientObservable : BaseObservable() { fun postNdkDeliverPending() { updateState { StateEvent.DeliverPending } } + + fun postSynchronizeState() { + updateState { StateEvent.SynchronizeState } + } } diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/DataCollectionModule.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/DataCollectionModule.kt index 4b9edee875..f81c826aeb 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/DataCollectionModule.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/DataCollectionModule.kt @@ -2,6 +2,7 @@ package com.bugsnag.android import android.os.Environment import com.bugsnag.android.internal.BackgroundTaskService +import com.bugsnag.android.internal.RootDetectionProvider import com.bugsnag.android.internal.dag.BackgroundDependencyModule import com.bugsnag.android.internal.dag.ConfigModule import com.bugsnag.android.internal.dag.ContextModule @@ -19,8 +20,9 @@ internal class DataCollectionModule( trackerModule: TrackerModule, bgTaskService: BackgroundTaskService, connectivity: Connectivity, - deviceIdStore: Provider, - memoryTrimState: MemoryTrimState + deviceId: Provider, + memoryTrimState: MemoryTrimState, + clientObservable: ClientObservable ) : BackgroundDependencyModule(bgTaskService) { private val ctx = contextModule.ctx @@ -41,17 +43,15 @@ internal class DataCollectionModule( ) } - private val rootDetection = provider { - val rootDetector = RootDetector(logger = logger, deviceBuildInfo = deviceBuildInfo) - rootDetector.isRooted() - } + private val rootDetection = RootDetectionProvider(deviceBuildInfo, clientObservable, logger) + .apply { start() } val deviceDataCollector = provider { DeviceDataCollector( connectivity, ctx, ctx.resources, - deviceIdStore.map { it.load() }, + deviceId, deviceBuildInfo, dataDir, rootDetection, diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceDataCollector.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceDataCollector.kt index a2e4a06f84..6557e7fe29 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceDataCollector.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceDataCollector.kt @@ -122,9 +122,10 @@ internal class DeviceDataCollector( } private fun checkIsRooted(): Boolean { + val rooted = rootedFuture ?: return false return try { - rootedFuture != null && rootedFuture.get() - } catch (exc: Exception) { + rooted.isComplete && rooted.get() + } catch (_: Exception) { false } } diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceIdStore.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceIdStore.kt index a0a2cab06f..922e3c9c0e 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceIdStore.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceIdStore.kt @@ -62,6 +62,7 @@ internal class DeviceIdStore @JvmOverloads @Suppress("LongParameterList") constr return internalPersistence.loadDeviceId(true) } + @Synchronized fun load(): DeviceIds? { if (deviceIds != null) { return deviceIds diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/StateEvent.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/StateEvent.kt index 4a227f9547..d45546dd4e 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/StateEvent.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/StateEvent.kt @@ -82,4 +82,6 @@ sealed class StateEvent { // JvmField allows direct field access optimizations ) : StateEvent() object ClearFeatureFlags : StateEvent() + + object SynchronizeState : StateEvent() } diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/StorageModule.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/StorageModule.kt index 46a07da023..7f230ac1af 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/StorageModule.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/StorageModule.kt @@ -34,11 +34,15 @@ internal class StorageModule( ) } + val deviceId = deviceIdStore.map { + it.load() + } + val userStore = provider { UserStore( immutableConfig.persistUser, bugsnagDir, - deviceIdStore.map { it.load() }, + deviceId, sharedPrefMigrator = sharedPrefMigrator, logger = immutableConfig.logger ) diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/RootDetectionProvider.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/RootDetectionProvider.kt new file mode 100644 index 0000000000..34176cd999 --- /dev/null +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/RootDetectionProvider.kt @@ -0,0 +1,32 @@ +package com.bugsnag.android.internal + +import com.bugsnag.android.ClientObservable +import com.bugsnag.android.DeviceBuildInfo +import com.bugsnag.android.Logger +import com.bugsnag.android.RootDetector +import com.bugsnag.android.internal.dag.RunnableProvider + +internal class RootDetectionProvider( + private val deviceBuildInfo: DeviceBuildInfo, + private val clientObservable: ClientObservable, + private val logger: Logger, +) : RunnableProvider() { + var isRooted: Boolean = false + private set + + fun start() { + // root detection can take 100+ms so we always have a dedicated background thread for it + // we fire an event once we're finished to let any downstream notifiers know the result + val worker = Thread(this, "Bugsnag Worker") + worker.priority = Thread.MIN_PRIORITY + worker.isDaemon = true + worker.start() + } + + override fun invoke(): Boolean { + val rootDetector = RootDetector(logger = logger, deviceBuildInfo = deviceBuildInfo) + isRooted = rootDetector.isRooted() + clientObservable.postSynchronizeState() + return isRooted + } +} diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/ConfigModule.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/ConfigModule.kt index aece93e476..503b3d6d8a 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/ConfigModule.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/ConfigModule.kt @@ -14,6 +14,6 @@ internal class ConfigModule( configuration: Configuration, connectivity: Connectivity, bgTaskExecutor: BackgroundTaskService -) : BackgroundDependencyModule(bgTaskExecutor) { +) : DependencyModule { val config = sanitiseConfiguration(contextModule.ctx, configuration, connectivity, bgTaskExecutor) } diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/ContextModule.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/ContextModule.kt index 0d36ce64bf..6947d11ae8 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/ContextModule.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/ContextModule.kt @@ -1,17 +1,12 @@ package com.bugsnag.android.internal.dag import android.content.Context -import com.bugsnag.android.internal.BackgroundTaskService /** * A dependency module which accesses the application context object, falling back to the supplied * context if it is the base context. */ -internal class ContextModule( - appContext: Context, - bgTaskService: BackgroundTaskService -) : BackgroundDependencyModule(bgTaskService) { - +internal class ContextModule(appContext: Context) : DependencyModule { val ctx: Context = when (appContext.applicationContext) { null -> appContext else -> appContext.applicationContext diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/Provider.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/Provider.kt index 6eb4408439..2d50a739b0 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/Provider.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/Provider.kt @@ -21,6 +21,8 @@ interface Provider { * to calculate the value. */ fun get(): E + + val isComplete: Boolean } /** @@ -40,6 +42,12 @@ abstract class RunnableProvider : Provider, Runnable { @Volatile private var value: Any? = null + override val isComplete + get() = when (state.get()) { + TASK_STATE_PENDING, TASK_STATE_RUNNING -> false + else -> true + } + /** * Calculate the value of this [Provider]. This function will be called at-most once by [run]. * Do not call this function directly, instead use [get] and [getOrNull] which implement the @@ -91,18 +99,13 @@ abstract class RunnableProvider : Provider, Runnable { */ private fun awaitResult() { synchronized(this) { - while (!isComplete()) { + while (!isComplete) { @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") (this as Object).wait() } } } - private fun isComplete() = when (state.get()) { - TASK_STATE_PENDING, TASK_STATE_RUNNING -> false - else -> true - } - /** * The main entry point for a provider, typically called by a worker thread from * [BackgroundTaskService]. If [run] has already been called this will be a no-op (including @@ -178,4 +181,5 @@ abstract class RunnableProvider : Provider, Runnable { data class ValueProvider(private val value: T) : Provider { override fun getOrNull(): T? = get() override fun get(): T = value + override val isComplete: Boolean get() = true } diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/SystemServiceModule.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/SystemServiceModule.kt index 1c7cd6cf02..1ff3a2dda3 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/SystemServiceModule.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/SystemServiceModule.kt @@ -2,16 +2,11 @@ package com.bugsnag.android.internal.dag import com.bugsnag.android.getActivityManager import com.bugsnag.android.getStorageManager -import com.bugsnag.android.internal.BackgroundTaskService /** * A dependency module which provides a reference to Android system services. */ -internal class SystemServiceModule( - contextModule: ContextModule, - bgTaskService: BackgroundTaskService -) : BackgroundDependencyModule(bgTaskService) { - +internal class SystemServiceModule(contextModule: ContextModule) : DependencyModule { val storageManager = contextModule.ctx.getStorageManager() val activityManager = contextModule.ctx.getActivityManager() } diff --git a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt index bd8f5a43f3..e58ce92e32 100644 --- a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt +++ b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt @@ -73,6 +73,7 @@ class NativeBridge(private val bgTaskService: BackgroundTaskService) : StateObse } private external fun addBreadcrumb(name: String, type: Int, timestamp: String, metadata: Any) + private external fun synchronizeState() external fun reportOutOfMemory(oom: OutOfMemoryError) external fun addMetadataString(tab: String, key: String, value: String) external fun addMetadataDouble(tab: String, key: String, value: Double) @@ -171,6 +172,8 @@ class NativeBridge(private val bgTaskService: BackgroundTaskService) : StateObse is StateEvent.ClearFeatureFlag -> clearFeatureFlag(event.name) is StateEvent.ClearFeatureFlags -> clearFeatureFlags() + is StateEvent.SynchronizeState -> synchronizeState() + else -> { // ignore } diff --git a/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c b/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c index 6a68284de2..aefc71d681 100644 --- a/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c +++ b/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c @@ -958,6 +958,19 @@ static void JNI_NativeBridge_setInternalMetricsEnabled(JNIEnv *env, bsg_set_internal_metrics_enabled(enabled); } +static void JNI_NativeBridge_synchronizeState(JNIEnv *env, jobject thiz) { + bsg_environment *bsg_env = request_env_write_lock(); + + if (bsg_env == NULL) { + goto end; + } + + bsg_populate_event(env, &bsg_env->next_event); + +end: + release_env_write_lock(); +} + static void JNI_NativeBridge_reportOutOfMemory(JNIEnv *env, jobject thiz, jobject oom) { diff --git a/bugsnag-plugin-android-ndk/src/main/jni/event.c b/bugsnag-plugin-android-ndk/src/main/jni/event.c index 7ba3320b80..be5291e0ef 100644 --- a/bugsnag-plugin-android-ndk/src/main/jni/event.c +++ b/bugsnag-plugin-android-ndk/src/main/jni/event.c @@ -339,7 +339,7 @@ char *bugsnag_event_get_context(void *event_ptr) { void bugsnag_event_set_context(void *event_ptr, const char *value) { bugsnag_event *event = (bugsnag_event *)event_ptr; bsg_notify_api_called(event, BSG_API_EVENT_SET_CONTEXT); - bsg_strncpy(event->context, value, sizeof(event->context)); + bsg_strncpy(event->context, value, sizeof(event->context) - 1); } char *bugsnag_event_get_grouping_discriminator(void *event_ptr) { diff --git a/bugsnag-plugin-android-ndk/src/main/jni/metadata.c b/bugsnag-plugin-android-ndk/src/main/jni/metadata.c index c6c8235b75..f3d8344ab3 100644 --- a/bugsnag-plugin-android-ndk/src/main/jni/metadata.c +++ b/bugsnag-plugin-android-ndk/src/main/jni/metadata.c @@ -174,7 +174,7 @@ static void populate_app_data(JNIEnv *env, bugsnag_event *event) { copy_map_value_string(env, data, "id", event->app.id, sizeof(event->app.id)); event->app.in_foreground = get_map_value_bool(env, data, "inForeground"); - event->app.is_launching = true; + event->app.is_launching = get_map_value_bool(env, data, "isLaunching"); char name[64]; copy_map_value_string(env, data, "name", name, sizeof(name)); diff --git a/features/fixtures/mazerunner/cxx-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/CXXMarkLaunchCompletedScenario.kt b/features/fixtures/mazerunner/cxx-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/CXXMarkLaunchCompletedScenario.kt index 03af8158d5..3b7dc1965f 100644 --- a/features/fixtures/mazerunner/cxx-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/CXXMarkLaunchCompletedScenario.kt +++ b/features/fixtures/mazerunner/cxx-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/CXXMarkLaunchCompletedScenario.kt @@ -1,11 +1,12 @@ package com.bugsnag.android.mazerunner.scenarios import android.content.Context +import android.os.Handler +import android.os.Looper import com.bugsnag.android.Bugsnag import com.bugsnag.android.Configuration import com.bugsnag.android.createDefaultDelivery import com.bugsnag.android.mazerunner.InterceptingDelivery -import java.util.concurrent.atomic.AtomicInteger /** * Sends an NDK error to Bugsnag after markLaunchCompleted() is invoked. @@ -16,17 +17,15 @@ internal class CXXMarkLaunchCompletedScenario( eventMetadata: String? ) : Scenario(config, context, eventMetadata) { - private val deliveryCount = AtomicInteger(0) - external fun crash() init { - config.launchDurationMillis = 0 System.loadLibrary("cxx-scenarios") - // wait for Bugsnag.notify() to complete before triggering NDK crash - config.delivery = InterceptingDelivery(createDefaultDelivery()) { - if (deliveryCount.incrementAndGet() == 1) { + config.launchDurationMillis = 0 + + config.delivery = InterceptingDelivery(createDefaultDelivery()) { result -> + Handler(Looper.getMainLooper()).post { crash() } } diff --git a/features/full_tests/native_api.feature b/features/full_tests/native_api.feature index ffbb704173..7688983051 100644 --- a/features/full_tests/native_api.feature +++ b/features/full_tests/native_api.feature @@ -12,7 +12,7 @@ Feature: Native API | SIGILL | | SIGTRAP | And the event "app.version" equals "22.312.749.78.300.810.24.167.32" - And the event "context" equals "ObservableSessionInitializerStringParserStringSessionProxyGloba" + And the event "context" equals "ObservableSessionInitializerStringParserStringSessionProxyGlob" And the event "unhandled" is true Scenario: Use the NDK methods without "env" after calling "bugsnag_start"