Skip to content

Commit 5a6fd3e

Browse files
authored
Merge pull request #2391 from bugsnag/PLAT-15668/bg-root-detection
Prevent root/jailbreak detection from blocking startup
2 parents de4a59a + 696a8be commit 5a6fd3e

21 files changed

Lines changed: 107 additions & 46 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
* 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).
88
[#2384](https://github.com/bugsnag/bugsnag-android/pull/2384)
99
* Added `appHangCooldownMillis` to the AppHangPlugin to control the number of AppHang errors produced when the app is performance constrained
10-
[]()
10+
[#2389](https://github.com/bugsnag/bugsnag-android/pull/2389)
11+
* Moved root/jailbreak detection onto a background thread so that it no longer blocks startup (this should improve startup performance in most common cases)
12+
[#2391](https://github.com/bugsnag/bugsnag-android/pull/2391)
1113

1214
## 6.24.0 (2026-02-11)
1315

bugsnag-android-core/api/bugsnag-android-core.api

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,10 @@ public final class com/bugsnag/android/StateEvent$StartSession : com/bugsnag/and
877877
public final fun getUnhandledCount ()I
878878
}
879879

880+
public final class com/bugsnag/android/StateEvent$SynchronizeState : com/bugsnag/android/StateEvent {
881+
public static final field INSTANCE Lcom/bugsnag/android/StateEvent$SynchronizeState;
882+
}
883+
880884
public final class com/bugsnag/android/StateEvent$UpdateContext : com/bugsnag/android/StateEvent {
881885
public final field context Ljava/lang/String;
882886
public fun <init> (Ljava/lang/String;)V
@@ -1179,13 +1183,15 @@ public final class com/bugsnag/android/internal/TaskType : java/lang/Enum {
11791183
public abstract interface class com/bugsnag/android/internal/dag/Provider {
11801184
public abstract fun get ()Ljava/lang/Object;
11811185
public abstract fun getOrNull ()Ljava/lang/Object;
1186+
public abstract fun isComplete ()Z
11821187
}
11831188

11841189
public abstract class com/bugsnag/android/internal/dag/RunnableProvider : com/bugsnag/android/internal/dag/Provider, java/lang/Runnable {
11851190
public fun <init> ()V
11861191
public fun get ()Ljava/lang/Object;
11871192
public fun getOrNull ()Ljava/lang/Object;
11881193
public abstract fun invoke ()Ljava/lang/Object;
1194+
public fun isComplete ()Z
11891195
public final fun run ()V
11901196
}
11911197

@@ -1197,6 +1203,7 @@ public final class com/bugsnag/android/internal/dag/ValueProvider : com/bugsnag/
11971203
public fun get ()Ljava/lang/Object;
11981204
public fun getOrNull ()Ljava/lang/Object;
11991205
public fun hashCode ()I
1206+
public fun isComplete ()Z
12001207
public fun toString ()Ljava/lang/String;
12011208
}
12021209

bugsnag-android-core/detekt-baseline.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<ID>LongParameterList:AppWithState.kt$AppWithState$( binaryArch: String?, id: String?, releaseStage: String?, version: String?, codeBundleId: String?, buildUuid: Provider&lt;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? )</ID>
1616
<ID>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? )</ID>
1717
<ID>LongParameterList:AppWithState.kt$AppWithState$( config: ImmutableConfig, binaryArch: String?, id: String?, releaseStage: String?, version: String?, codeBundleId: String?, duration: Number?, durationInForeground: Number?, inForeground: Boolean?, isLaunching: Boolean? )</ID>
18-
<ID>LongParameterList:DataCollectionModule.kt$DataCollectionModule$( contextModule: ContextModule, configModule: ConfigModule, systemServiceModule: SystemServiceModule, trackerModule: TrackerModule, bgTaskService: BackgroundTaskService, connectivity: Connectivity, deviceIdStore: Provider&lt;DeviceIdStore>, memoryTrimState: MemoryTrimState )</ID>
18+
<ID>LongParameterList:DataCollectionModule.kt$DataCollectionModule$( contextModule: ContextModule, configModule: ConfigModule, systemServiceModule: SystemServiceModule, trackerModule: TrackerModule, bgTaskService: BackgroundTaskService, connectivity: Connectivity, deviceId: Provider&lt;DeviceIdStore.DeviceIds?>, memoryTrimState: MemoryTrimState, clientObservable: ClientObservable )</ID>
1919
<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>
2020
<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>
2121
<ID>LongParameterList:DeviceDataCollector.kt$DeviceDataCollector$( private val connectivity: Connectivity, private val appContext: Context, resources: Resources, private val deviceIdStore: Provider&lt;DeviceIdStore.DeviceIds?>, private val buildInfo: DeviceBuildInfo, private val dataDirectory: File, private val rootedFuture: Provider&lt;Boolean>?, private val bgTaskService: BackgroundTaskService, private val logger: Logger )</ID>
@@ -84,7 +84,6 @@
8484
<ID>SwallowedException:ConnectivityCompat.kt$ConnectivityLegacy$e: NullPointerException</ID>
8585
<ID>SwallowedException:ContextExtensions.kt$exc: RuntimeException</ID>
8686
<ID>SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$e: Throwable</ID>
87-
<ID>SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$exc: Exception</ID>
8887
<ID>SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$exception: Exception</ID>
8988
<ID>SwallowedException:DeviceIdFilePersistence.kt$DeviceIdFilePersistence$exc: OverlappingFileLockException</ID>
9089
<ID>SwallowedException:EventStore.kt$EventStore$exception: RejectedExecutionException</ID>

bugsnag-android-core/src/main/java/com/bugsnag/android/Client.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public Client(@NonNull Context androidContext, @NonNull String apiKey) {
128128
* @param configuration a configuration for the Client
129129
*/
130130
public Client(@NonNull Context androidContext, @NonNull final Configuration configuration) {
131-
ContextModule contextModule = new ContextModule(androidContext, bgTaskService);
131+
ContextModule contextModule = new ContextModule(androidContext);
132132
appContext = contextModule.getCtx();
133133

134134
notifier = configuration.getNotifier();
@@ -186,16 +186,16 @@ public Unit invoke(Boolean hasConnection, String networkState) {
186186

187187
// lookup system services
188188
final SystemServiceModule systemServiceModule =
189-
new SystemServiceModule(contextModule, bgTaskService);
189+
new SystemServiceModule(contextModule);
190190

191191
// setup further state trackers and data collection
192192
TrackerModule trackerModule = new TrackerModule(configModule,
193193
storageModule, this, bgTaskService, callbackState);
194194

195195
DataCollectionModule dataCollectionModule = new DataCollectionModule(contextModule,
196196
configModule, systemServiceModule, trackerModule,
197-
bgTaskService, connectivity, storageModule.getDeviceIdStore(),
198-
memoryTrimState);
197+
bgTaskService, connectivity, storageModule.getDeviceId(),
198+
memoryTrimState, clientObservable);
199199

200200
// load the device + user information
201201
userState = storageModule.loadUser(configuration.getUser());

bugsnag-android-core/src/main/java/com/bugsnag/android/ClientObservable.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@ internal class ClientObservable : BaseObservable() {
3131
fun postNdkDeliverPending() {
3232
updateState { StateEvent.DeliverPending }
3333
}
34+
35+
fun postSynchronizeState() {
36+
updateState { StateEvent.SynchronizeState }
37+
}
3438
}

bugsnag-android-core/src/main/java/com/bugsnag/android/DataCollectionModule.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.bugsnag.android
22

33
import android.os.Environment
44
import com.bugsnag.android.internal.BackgroundTaskService
5+
import com.bugsnag.android.internal.RootDetectionProvider
56
import com.bugsnag.android.internal.dag.BackgroundDependencyModule
67
import com.bugsnag.android.internal.dag.ConfigModule
78
import com.bugsnag.android.internal.dag.ContextModule
@@ -19,8 +20,9 @@ internal class DataCollectionModule(
1920
trackerModule: TrackerModule,
2021
bgTaskService: BackgroundTaskService,
2122
connectivity: Connectivity,
22-
deviceIdStore: Provider<DeviceIdStore>,
23-
memoryTrimState: MemoryTrimState
23+
deviceId: Provider<DeviceIdStore.DeviceIds?>,
24+
memoryTrimState: MemoryTrimState,
25+
clientObservable: ClientObservable
2426
) : BackgroundDependencyModule(bgTaskService) {
2527

2628
private val ctx = contextModule.ctx
@@ -41,17 +43,15 @@ internal class DataCollectionModule(
4143
)
4244
}
4345

44-
private val rootDetection = provider {
45-
val rootDetector = RootDetector(logger = logger, deviceBuildInfo = deviceBuildInfo)
46-
rootDetector.isRooted()
47-
}
46+
private val rootDetection = RootDetectionProvider(deviceBuildInfo, clientObservable, logger)
47+
.apply { start() }
4848

4949
val deviceDataCollector = provider {
5050
DeviceDataCollector(
5151
connectivity,
5252
ctx,
5353
ctx.resources,
54-
deviceIdStore.map { it.load() },
54+
deviceId,
5555
deviceBuildInfo,
5656
dataDir,
5757
rootDetection,

bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceDataCollector.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,10 @@ internal class DeviceDataCollector(
122122
}
123123

124124
private fun checkIsRooted(): Boolean {
125+
val rooted = rootedFuture ?: return false
125126
return try {
126-
rootedFuture != null && rootedFuture.get()
127-
} catch (exc: Exception) {
127+
rooted.isComplete && rooted.get()
128+
} catch (_: Exception) {
128129
false
129130
}
130131
}

bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceIdStore.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ internal class DeviceIdStore @JvmOverloads @Suppress("LongParameterList") constr
6262
return internalPersistence.loadDeviceId(true)
6363
}
6464

65+
@Synchronized
6566
fun load(): DeviceIds? {
6667
if (deviceIds != null) {
6768
return deviceIds

bugsnag-android-core/src/main/java/com/bugsnag/android/StateEvent.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,6 @@ sealed class StateEvent { // JvmField allows direct field access optimizations
8282
) : StateEvent()
8383

8484
object ClearFeatureFlags : StateEvent()
85+
86+
object SynchronizeState : StateEvent()
8587
}

bugsnag-android-core/src/main/java/com/bugsnag/android/StorageModule.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,15 @@ internal class StorageModule(
3434
)
3535
}
3636

37+
val deviceId = deviceIdStore.map {
38+
it.load()
39+
}
40+
3741
val userStore = provider {
3842
UserStore(
3943
immutableConfig.persistUser,
4044
bugsnagDir,
41-
deviceIdStore.map { it.load() },
45+
deviceId,
4246
sharedPrefMigrator = sharedPrefMigrator,
4347
logger = immutableConfig.logger
4448
)

0 commit comments

Comments
 (0)