Skip to content

Commit ec5e3a5

Browse files
romtsnclaude
andauthored
fix(android): Fix crash when getHistoricalProcessStartReasons is called from a wrong process (#5597)
* fix(android): Fix crash when getHistoricalProcessStartReasons is called from a wrong process * test(android): Add test and changelog for getHistoricalProcessStartReasons crash fix Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * SecurityException -> RuntimeException --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f982bad commit ec5e3a5

4 files changed

Lines changed: 43 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Fixes
66

7+
- Fix crash when `getHistoricalProcessStartReasons` is called from an isolated or wrong-userId process ([#5597](https://github.com/getsentry/sentry-java/pull/5597))
78
- Release `MediaMuxer` when a replay segment has no encodable frames to avoid a resource leak ([#5583](https://github.com/getsentry/sentry-java/pull/5583))
89

910
## 8.44.1

sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import android.os.Handler;
1212
import android.os.Looper;
1313
import android.os.SystemClock;
14+
import android.util.Log;
1415
import androidx.annotation.NonNull;
1516
import androidx.annotation.Nullable;
1617
import androidx.annotation.VisibleForTesting;
@@ -467,18 +468,28 @@ public void registerLifecycleCallbacks(final @NotNull Application application) {
467468
final @Nullable ActivityManager activityManager =
468469
(ActivityManager) application.getSystemService(Context.ACTIVITY_SERVICE);
469470
if (activityManager != null) {
470-
final List<ApplicationStartInfo> historicalProcessStartReasons =
471-
activityManager.getHistoricalProcessStartReasons(1);
472-
if (!historicalProcessStartReasons.isEmpty()) {
473-
final @NotNull ApplicationStartInfo info = historicalProcessStartReasons.get(0);
474-
cachedStartInfo = info;
475-
if (info.getStartupState() == ApplicationStartInfo.STARTUP_STATE_STARTED) {
476-
if (info.getStartType() == ApplicationStartInfo.START_TYPE_COLD) {
477-
appStartType = AppStartType.COLD;
478-
} else {
479-
appStartType = AppStartType.WARM;
471+
try {
472+
final List<ApplicationStartInfo> historicalProcessStartReasons =
473+
activityManager.getHistoricalProcessStartReasons(1);
474+
if (!historicalProcessStartReasons.isEmpty()) {
475+
final @NotNull ApplicationStartInfo info = historicalProcessStartReasons.get(0);
476+
cachedStartInfo = info;
477+
if (info.getStartupState() == ApplicationStartInfo.STARTUP_STATE_STARTED) {
478+
if (info.getStartType() == ApplicationStartInfo.START_TYPE_COLD) {
479+
appStartType = AppStartType.COLD;
480+
} else {
481+
appStartType = AppStartType.WARM;
482+
}
480483
}
481484
}
485+
} catch (RuntimeException ignored) {
486+
// getHistoricalProcessStartReasons may throw different kinds of exceptions, namely:
487+
// - SecurityException when called from an isolated process
488+
// - IllegalArgumentException when called with a wrong userId
489+
// - others
490+
// See impl:
491+
// https://cs.android.com/android/platform/superproject/+/android-latest-release:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java;l=10866-10893
492+
Log.w("AppStartMetrics", ignored); // no logger instance here, so we just Log
482493
}
483494
}
484495
}

sentry-android-core/src/test/java/io/sentry/android/core/SentryShadowActivityManager.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,24 @@ class SentryShadowActivityManager {
1212
companion object {
1313
private var historicalProcessStartReasons: List<ApplicationStartInfo> = emptyList()
1414
private var importance: Int = RunningAppProcessInfo.IMPORTANCE_FOREGROUND
15+
private var historicalProcessStartReasonsException: RuntimeException? = null
1516

1617
fun setHistoricalProcessStartReasons(startReasons: List<ApplicationStartInfo>) {
1718
historicalProcessStartReasons = startReasons
1819
}
1920

21+
fun setHistoricalProcessStartReasonsException(exception: RuntimeException) {
22+
historicalProcessStartReasonsException = exception
23+
}
24+
2025
fun setImportance(importance: Int) {
2126
this.importance = importance
2227
}
2328

2429
fun reset() {
2530
historicalProcessStartReasons = emptyList()
2631
importance = RunningAppProcessInfo.IMPORTANCE_FOREGROUND
32+
historicalProcessStartReasonsException = null
2733
}
2834

2935
@Implementation
@@ -35,6 +41,7 @@ class SentryShadowActivityManager {
3541

3642
@Implementation
3743
fun getHistoricalProcessStartReasons(maxNum: Int): List<ApplicationStartInfo> {
44+
historicalProcessStartReasonsException?.let { throw it }
3845
return historicalProcessStartReasons.take(maxNum)
3946
}
4047
}

sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTestApi35.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,20 @@ class AppStartMetricsTestApi35 {
249249
assertNull(metrics.appStartReason)
250250
}
251251

252+
@Test
253+
fun `does not crash when getHistoricalProcessStartReasons throws RuntimeException`() {
254+
SentryShadowActivityManager.setHistoricalProcessStartReasonsException(
255+
RuntimeException("isolated process")
256+
)
257+
val metrics = AppStartMetrics.getInstance()
258+
259+
val app = ApplicationProvider.getApplicationContext<Application>()
260+
metrics.registerLifecycleCallbacks(app)
261+
262+
assertEquals(AppStartMetrics.AppStartType.UNKNOWN, metrics.appStartType)
263+
assertNull(metrics.appStartReason)
264+
}
265+
252266
private fun waitForMainLooperIdle() {
253267
Handler(Looper.getMainLooper()).post {}
254268
Shadows.shadowOf(Looper.getMainLooper()).idle()

0 commit comments

Comments
 (0)