Skip to content

Commit

Permalink
Allow early session starts in auto mode (#2006)
Browse files Browse the repository at this point in the history
* feat(session)Allow early session starts in auto mode

* feat(session)Allow early session starts in auto mode

* feat(session)End to end test

* feat(feature flags): don't copy FeatureFlags on the crashing thread

* feat(session)Allow early session starts in auto mode

---------

Co-authored-by: jason <[email protected]>
  • Loading branch information
YYChen01988 and lemnik authored Apr 10, 2024
1 parent cabac65 commit 43e4cc8
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
* FeatureFlags are now a copy-on-write structure, and so don't need to be defensive copied on a crashing thread
[#2005](https://github.com/bugsnag/bugsnag-android/pull/2005)

* Allow `Bugsnag.startSession` to be called with automatic session tracking, and not have the first manual session be over written by the first automatic session.
[#2006](https://github.com/bugsnag/bugsnag-android/pull/2006)

## 6.3.0 (2024-03-19)

### Enhancements
Expand Down
1 change: 0 additions & 1 deletion bugsnag-android-core/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
<ID>LongParameterList:Device.kt$Device$( buildInfo: DeviceBuildInfo, /** * The Application Binary Interface used */ var cpuAbi: Array&lt;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&lt;String, Any>? )</ID>
<ID>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&lt;String>? )</ID>
<ID>LongParameterList:DeviceDataCollector.kt$DeviceDataCollector$( private val connectivity: Connectivity, private val appContext: Context, resources: Resources, private val deviceId: String?, private val internalDeviceId: String?, private val buildInfo: DeviceBuildInfo, private val dataDirectory: File, rootDetector: RootDetector, private val bgTaskService: BackgroundTaskService, private val logger: Logger )</ID>
<ID>LongParameterList:DeviceIdStore.kt$DeviceIdStore$( context: Context, deviceIdfile: File = File(context.filesDir, "device-id"), deviceIdGenerator: () -> UUID = { UUID.randomUUID() }, internalDeviceIdfile: File = File(context.filesDir, "internal-device-id"), internalDeviceIdGenerator: () -> UUID = { UUID.randomUUID() }, private val sharedPrefMigrator: SharedPrefMigrator, logger: Logger )</ID>
<ID>LongParameterList:DeviceWithState.kt$DeviceWithState$( buildInfo: DeviceBuildInfo, jailbroken: Boolean?, id: String?, locale: String?, totalMemory: Long?, runtimeVersions: MutableMap&lt;String, Any>, /** * The number of free bytes of storage available on the device */ var freeDisk: Long?, /** * The number of free bytes of memory available on the device */ var freeMemory: Long?, /** * The orientation of the device when the event occurred: either portrait or landscape */ var orientation: String?, /** * The timestamp on the device when the event occurred */ var time: Date? )</ID>
<ID>LongParameterList:EventFilenameInfo.kt$EventFilenameInfo.Companion$( obj: Any, uuid: String = UUID.randomUUID().toString(), apiKey: String?, timestamp: Long = System.currentTimeMillis(), config: ImmutableConfig, isLaunching: Boolean? = null )</ID>
<ID>LongParameterList:EventInternal.kt$EventInternal$( apiKey: String, logger: Logger, breadcrumbs: MutableList&lt;Breadcrumb> = mutableListOf(), discardClasses: Set&lt;Pattern> = setOf(), errors: MutableList&lt;Error> = mutableListOf(), metadata: Metadata = Metadata(), featureFlags: FeatureFlags = FeatureFlags(), originalError: Throwable? = null, projectPackages: Collection&lt;String> = setOf(), severityReason: SeverityReason = SeverityReason.newInstance(SeverityReason.REASON_HANDLED_EXCEPTION), threads: MutableList&lt;Thread> = mutableListOf(), user: User = User(), redactionKeys: Set&lt;Pattern>? = null )</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class SessionTracker extends BaseObservable implements ForegroundDetector.OnActi
private volatile Session currentSession = null;
final BackgroundTaskService backgroundTaskService;
final Logger logger;
private boolean shouldSuppressFirstAutoSession = false;

SessionTracker(ImmutableConfig configuration,
CallbackState callbackState,
Expand Down Expand Up @@ -76,7 +77,7 @@ class SessionTracker extends BaseObservable implements ForegroundDetector.OnActi
@VisibleForTesting
Session startNewSession(@NonNull Date date, @Nullable User user,
boolean autoCaptured) {
if (client.getConfig().shouldDiscardSession(autoCaptured)) {
if (shouldDiscardSession(autoCaptured)) {
return null;
}
String id = UUID.randomUUID().toString();
Expand All @@ -92,12 +93,29 @@ Session startNewSession(@NonNull Date date, @Nullable User user,
}

Session startSession(boolean autoCaptured) {
if (client.getConfig().shouldDiscardSession(autoCaptured)) {
if (shouldDiscardSession(autoCaptured)) {
return null;
}
return startNewSession(new Date(), client.getUser(), autoCaptured);
}

private boolean shouldDiscardSession(boolean autoCaptured) {
if (client.getConfig().shouldDiscardSession(autoCaptured)) {
return true;
} else {
Session existingSession = currentSession;
if (autoCaptured
&& existingSession != null
&& !existingSession.isAutoCaptured()
&& shouldSuppressFirstAutoSession) {
shouldSuppressFirstAutoSession = true;
return true;
}
}
return false;
}


void pauseSession() {
Session session = currentSession;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class MazerunnerApp : Application() {
super.onCreate()
triggerStartupAnrIfRequired()
setupNonSdkUsageStrictMode()
triggerManualSessionIfRequired()
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.bugsnag.android.mazerunner

import android.app.Application
import com.bugsnag.android.Bugsnag
import com.bugsnag.android.Configuration
import com.bugsnag.android.EndpointConfiguration

fun Application.triggerManualSessionIfRequired() {
val prefs = getSharedPreferences("SessionPreferences", android.content.Context.MODE_PRIVATE)
val manualSession = prefs.getBoolean("manualSession", false)

if (manualSession) {
val notifyEndpoint = prefs.getString("notify", null)
val sessionsEndpoint = prefs.getString("sessions", null)

// we remove the preferences so that we don't affect any future startup
prefs.edit()
.remove("notify")
.remove("sessions")
.remove("manualSession")
.commit()

// we have to startup Bugsnag at this point
val config = Configuration.load(this)
if (!notifyEndpoint.isNullOrBlank() && !sessionsEndpoint.isNullOrBlank()) {
config.endpoints = EndpointConfiguration(notifyEndpoint, sessionsEndpoint)
}

Bugsnag.start(this, config)
Bugsnag.startSession()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.bugsnag.android.mazerunner.scenarios

import android.content.Context
import com.bugsnag.android.Configuration
import kotlin.system.exitProcess

internal class StartSessionAutoModeScenario(
config: Configuration,
context: Context,
eventMetadata: String?
) : Scenario(config, context, eventMetadata) {
override fun startScenario() {
context.applicationContext
.getSharedPreferences("SessionPreferences", Context.MODE_PRIVATE)
.edit()
.putBoolean("manualSession", MANUAL_START)
.putString("notify", config.endpoints.notify)
.putString("sessions", config.endpoints.sessions)
.commit()

exitProcess(0)
}

companion object {
private const val MANUAL_START = true
}
}
5 changes: 5 additions & 0 deletions features/smoke_tests/03_sessions.feature
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,8 @@ Feature: Session functionality smoke tests
And the event "user.email" equals "ABC.CBA.CA"
And the event "user.name" equals "ManualSessionSmokeScenario"

Scenario: Start session in auto mode
When I clear any error dialogue
And I run "StartSessionAutoModeScenario"
And I relaunch the app after a crash
Then I wait to receive a session

0 comments on commit 43e4cc8

Please sign in to comment.