Skip to content

Commit 2963269

Browse files
committed
fix(root): add a process timeout to RootDetector
1 parent eca2499 commit 2963269

File tree

3 files changed

+39
-0
lines changed

3 files changed

+39
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
* The `bugsnag-plugin-android-exitinfo` plugin now calls `setProcessState` (if configured) on a background thread and swallows any rate-limiting errors, so that it does not block the main thread during startup
88
[#2197](https://github.com/bugsnag/bugsnag-android/pull/2197)
9+
* Fixed a background ANR that could occur during startup if processes do not launch or run quickly enough
10+
[#2201](https://github.com/bugsnag/bugsnag-android/pull/2201)
911

1012
## 6.14.0 (2025-06-04)
1113

bugsnag-android-core/detekt-baseline.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
<ID>SwallowedException:ForegroundDetector.kt$ForegroundDetector$e: Exception</ID>
6262
<ID>SwallowedException:JsonHelperTest.kt$JsonHelperTest$e: IllegalArgumentException</ID>
6363
<ID>SwallowedException:PluginClient.kt$PluginClient$exc: ClassNotFoundException</ID>
64+
<ID>SwallowedException:RootDetector.kt$RootDetector$ex: IllegalThreadStateException</ID>
6465
<ID>SwallowedException:SharedPrefMigrator.kt$SharedPrefMigrator$e: RuntimeException</ID>
6566
<ID>ThrowsCount:JsonHelper.kt$JsonHelper$fun jsonToLong(value: Any?): Long?</ID>
6667
<ID>TooManyFunctions:ConfigInternal.kt$ConfigInternal : CallbackAwareMetadataAwareUserAwareFeatureFlagAware</ID>

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package com.bugsnag.android
22

3+
import android.os.Build
4+
import android.os.SystemClock
35
import androidx.annotation.VisibleForTesting
46
import java.io.File
57
import java.io.IOException
68
import java.io.Reader
9+
import java.lang.Thread
10+
import java.util.concurrent.TimeUnit
11+
import kotlin.math.min
712

813
/**
914
* Attempts to detect whether the device is rooted. Root detection errs on the side of false
@@ -21,6 +26,9 @@ internal class RootDetector @JvmOverloads constructor(
2126
) {
2227

2328
companion object {
29+
private const val PROCESS_TIMEOUT = 250L
30+
private const val PROCESS_POLL_DELAY = 50L
31+
2432
private val BUILD_PROP_FILE = File("/system/build.prop")
2533

2634
private val ROOT_INDICATORS = listOf(
@@ -120,7 +128,20 @@ internal class RootDetector @JvmOverloads constructor(
120128
var process: Process? = null
121129
return try {
122130
process = processBuilder.start()
131+
val processComplete = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
132+
process.waitFor(PROCESS_TIMEOUT, TimeUnit.MILLISECONDS)
133+
} else {
134+
process.fallbackWaitFor(PROCESS_TIMEOUT)
135+
}
136+
137+
if (!processComplete) {
138+
return false
139+
}
140+
123141
process.inputStream.bufferedReader().use { it.isNotBlank() }
142+
} catch (ignored: InterruptedException) {
143+
Thread.currentThread().interrupt() // restore the interrupted status
144+
false
124145
} catch (ignored: IOException) {
125146
false
126147
} finally {
@@ -147,4 +168,19 @@ internal class RootDetector @JvmOverloads constructor(
147168
libraryLoaded -> performNativeRootChecks()
148169
else -> false
149170
}
171+
172+
private fun Process.fallbackWaitFor(timeout: Long): Boolean {
173+
val endTime = SystemClock.elapsedRealtime() + timeout
174+
while (SystemClock.elapsedRealtime() < endTime) {
175+
try {
176+
exitValue()
177+
return true
178+
} catch (ex: IllegalThreadStateException) {
179+
// Process is still running, wait a bit before checking again
180+
Thread.sleep(min(PROCESS_POLL_DELAY, endTime - SystemClock.elapsedRealtime()))
181+
}
182+
}
183+
184+
return false
185+
}
150186
}

0 commit comments

Comments
 (0)