Skip to content

Commit 31b6fcc

Browse files
committed
fix(root): add a process timeout to RootDetector
1 parent 75693c8 commit 31b6fcc

3 files changed

Lines changed: 39 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
[#2197](https://github.com/bugsnag/bugsnag-android/pull/2197)
99
* Improve the scoping of the build-id capturing in `bugsnag-plugin-android-ndk` to more reliably capture the build-id from the correct `.so` file
1010
[#2203](https://github.com/bugsnag/bugsnag-android/pull/2203)
11+
* Fixed a background ANR that could occur during startup if processes do not launch or run quickly enough
12+
[#2202](https://github.com/bugsnag/bugsnag-android/pull/2202)
1113

1214
## 6.14.0 (2025-06-04)
1315

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)