From 50f86c9d65de6193310b39c6bd7ee2c212d1ea06 Mon Sep 17 00:00:00 2001 From: Matthew Robertson Date: Wed, 23 Sep 2026 14:28:47 -0400 Subject: [PATCH 1/4] Rename CrashlyticsOtelContext to TelemetryContext --- ...ContextTest.kt => TelemetryContextTest.kt} | 110 +++++++++--------- .../telemetry/OpenTelemetryManager.kt | 2 +- ...ticsOtelContext.kt => TelemetryContext.kt} | 9 +- 3 files changed, 61 insertions(+), 60 deletions(-) rename firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/{CrashlyticsOtelContextTest.kt => TelemetryContextTest.kt} (85%) rename firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/{CrashlyticsOtelContext.kt => TelemetryContext.kt} (94%) diff --git a/firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/CrashlyticsOtelContextTest.kt b/firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/TelemetryContextTest.kt similarity index 85% rename from firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/CrashlyticsOtelContextTest.kt rename to firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/TelemetryContextTest.kt index 8375135961e..e3f304597e2 100644 --- a/firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/CrashlyticsOtelContextTest.kt +++ b/firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/TelemetryContextTest.kt @@ -37,7 +37,7 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) -class CrashlyticsOtelContextTest { +class TelemetryContextTest { @get:Rule val otelRule: OpenTelemetryRule = OpenTelemetryRule.create() private lateinit var testMmapFile: File @@ -49,24 +49,24 @@ class CrashlyticsOtelContextTest { testMmapFile = File(targetContext.cacheDir, "shared_test_context.mmap") if (testMmapFile.exists()) testMmapFile.delete() - mutationContext = CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) + mutationContext = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) Span.getInvalid().makeCurrent() } @After fun tearDown() { - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() } - // --- 1. CrashlyticsOtelContext.initialize --- + // --- 1. TelemetryContext.initialize --- @Test fun initialize_createsMmapFile() { - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() - CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) + TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) assertThat(testMmapFile.exists()).isTrue() } @@ -79,10 +79,10 @@ class CrashlyticsOtelContextTest { fun initialize_withOnRecovery_returnsRecoveredSpans() { val originalSpan = createTestSpan("RecoverableSpan") mutationContext.addSpan(originalSpan) - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() val latch = CountDownLatch(1) - CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recoveredSpans -> + TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recoveredSpans -> assertThat(recoveredSpans.size).isEqualTo(1) latch.countDown() } @@ -93,7 +93,7 @@ class CrashlyticsOtelContextTest { fun initialize_withCorruptedNonTerminatedSpanName_recoversSafelyWithoutCrash() { val originalSpan = createTestSpan("InitialSpan") mutationContext.addSpan(originalSpan) - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() // Write 64 non-null bytes into RawSpan.name (offset 88) to test create_bounded_jstring handling // of non-terminated/oversized buffer @@ -104,7 +104,7 @@ class CrashlyticsOtelContextTest { val latch = CountDownLatch(1) var recoveredSpans: List? = null - CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> recoveredSpans = recovered latch.countDown() } @@ -118,16 +118,16 @@ class CrashlyticsOtelContextTest { @Test fun initialize_calledTwice_returnsSingleton() { - val secondContext = CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) + val secondContext = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) assertThat(secondContext).isSameInstanceAs(mutationContext) } @Test fun initialize_afterShutdown_createsNewInstance() { val firstContext = mutationContext - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() - val secondContext = CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) + val secondContext = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) assertThat(secondContext).isNotSameInstanceAs(firstContext) } @@ -137,16 +137,16 @@ class CrashlyticsOtelContextTest { val threadPool = Executors.newFixedThreadPool(threads) val countDownLatch = CountDownLatch(threads + 1) - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() val targetContext = InstrumentationRegistry.getInstrumentation().targetContext val recoveryMmapFile = File(targetContext.cacheDir, "concurrent_recovery.mmap") if (recoveryMmapFile.exists()) recoveryMmapFile.delete() val prePopulateContext = - CrashlyticsOtelContext.initialize(recoveryMmapFile.absolutePath, MmapSize.SMALL) + TelemetryContext.initialize(recoveryMmapFile.absolutePath, MmapSize.SMALL) prePopulateContext.addSpan(createThreadSafeTestSpan(1, 1)) - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() val recoveryInvocationCount = AtomicInteger(0) val onRecovery: suspend (List) -> Unit = { @@ -163,7 +163,7 @@ class CrashlyticsOtelContextTest { for (i in 0 until threads) { threadPool.submit { try { - CrashlyticsOtelContext.initialize( + TelemetryContext.initialize( recoveryMmapFile.absolutePath, MmapSize.SMALL, onRecovery, @@ -178,29 +178,29 @@ class CrashlyticsOtelContextTest { assertThat(recoveryInvocationCount.get()).isEqualTo(1) } finally { threadPool.shutdown() - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() if (recoveryMmapFile.exists()) recoveryMmapFile.delete() } } - // --- 2. CrashlyticsOtelContext.shutdown --- + // --- 2. TelemetryContext.shutdown --- @Test fun shutdown_beforeInitialize_doesNotThrow() { - CrashlyticsOtelContext.shutdown() // Tear down default - CrashlyticsOtelContext.shutdown() // Should run safely when already null + TelemetryContext.shutdown() // Tear down default + TelemetryContext.shutdown() // Should run safely when already null } @Test fun shutdown_clearsMutationContext() { - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() assertThat(mutationContext.countSpans()).isEqualTo(0L) } @Test fun shutdown_calledTwice_doesNotThrow() { - CrashlyticsOtelContext.shutdown() - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() + TelemetryContext.shutdown() } @Test @@ -213,7 +213,7 @@ class CrashlyticsOtelContextTest { for (i in 0 until threads) { threadPool.submit { try { - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() } finally { countDownLatch.countDown() } @@ -227,10 +227,10 @@ class CrashlyticsOtelContextTest { @Test fun shutdown_calledConcurrentlyWithMutations_doesNotCrashOrThrow() { - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() - val context = CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) + val context = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) val threads = 10 val iterations = 50 @@ -259,7 +259,7 @@ class CrashlyticsOtelContextTest { startLatch.countDown() Thread.sleep(5) - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() assertThat(countDownLatch.await(5, TimeUnit.SECONDS)).isTrue() } finally { @@ -283,7 +283,7 @@ class CrashlyticsOtelContextTest { @Test fun countSpans_afterShutdown_returnsZero() { - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() assertThat(mutationContext.countSpans()).isEqualTo(0L) } @@ -300,9 +300,9 @@ class CrashlyticsOtelContextTest { val originalSpan = createTestSpan("PersistedSpan", mapOf("k" to "v")) mutationContext.addSpan(originalSpan) - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() - CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> assertThat(recovered).isNotEmpty() } } @@ -313,11 +313,11 @@ class CrashlyticsOtelContextTest { val span = createTestSpan(oversizedName) mutationContext.addSpan(span) - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() val latch = CountDownLatch(1) var recoveredSpans: List? = null - CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> recoveredSpans = recovered latch.countDown() } @@ -336,11 +336,11 @@ class CrashlyticsOtelContextTest { val span = createTestSpan("OversizedAttrSpan", mapOf(oversizedKey to oversizedValue)) mutationContext.addSpan(span) - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() val latch = CountDownLatch(1) var recoveredSpans: List? = null - CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> recoveredSpans = recovered latch.countDown() } @@ -364,11 +364,11 @@ class CrashlyticsOtelContextTest { val span = createTestSpan(utf8Name, mapOf(utf8Key to utf8Value)) mutationContext.addSpan(span) - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() val latch = CountDownLatch(1) var recoveredSpans: List? = null - CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> recoveredSpans = recovered latch.countDown() } @@ -385,11 +385,11 @@ class CrashlyticsOtelContextTest { val span = createTestSpan("", emptyMap()) mutationContext.addSpan(span) - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() val latch = CountDownLatch(1) var recoveredSpans: List? = null - CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> recoveredSpans = recovered latch.countDown() } @@ -403,16 +403,16 @@ class CrashlyticsOtelContextTest { @Test fun addSpan_afterShutdown_isNoOp() { - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() mutationContext.addSpan(createTestSpan("AfterShutdown")) } @Test fun addSpan_calledConcurrently_maintainsAccurateCount() { - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() - val context = CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) + val context = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) val threads = 10 val iterations = 20 @@ -458,16 +458,16 @@ class CrashlyticsOtelContextTest { @Test fun endSpan_afterShutdown_isNoOp() { - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() mutationContext.endSpan("0000000000000000") } @Test fun endSpan_calledConcurrently_maintainsConsistentState() { - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() - val context = CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) + val context = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) val threads = 10 val iterations = 20 @@ -553,11 +553,11 @@ class CrashlyticsOtelContextTest { val oversizedValue = "v".repeat(300) mutationContext.setAttributeOnSpan(span.spanId, oversizedKey, oversizedValue) - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() val latch = CountDownLatch(1) var recoveredSpans: List? = null - CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> recoveredSpans = recovered latch.countDown() } @@ -579,16 +579,16 @@ class CrashlyticsOtelContextTest { @Test fun setAttributeOnSpan_afterShutdown_isNoOp() { - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() mutationContext.setAttributeOnSpan("0000000000000000", "key", "value") } @Test fun setAttributeOnSpan_calledConcurrently_doesNotCrashOrCorrupt() { - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() - val context = CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) + val context = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) val threads = 10 val iterations = 20 @@ -632,10 +632,10 @@ class CrashlyticsOtelContextTest { @Test fun mixedOperations_calledConcurrently_maintainsDataIntegrityAndRecoversCleanly() { - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() - val context = CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) + val context = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) val threads = 10 val iterations = 20 @@ -661,11 +661,11 @@ class CrashlyticsOtelContextTest { assertThat(countDownLatch.await(10, TimeUnit.SECONDS)).isTrue() assertThat(context.countSpans()).isEqualTo((threads * iterations).toLong()) - CrashlyticsOtelContext.shutdown() + TelemetryContext.shutdown() val recoveryLatch = CountDownLatch(1) var recoveredSpans: List? = null - CrashlyticsOtelContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) { recovered -> + TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) { recovered -> recoveredSpans = recovered recoveryLatch.countDown() } @@ -684,7 +684,7 @@ class CrashlyticsOtelContextTest { name: String, attributes: Map = emptyMap(), ): CrashlyticsSpan { - val tracer = otelRule.openTelemetry.getTracer("CrashlyticsOtelContextTest") + val tracer = otelRule.openTelemetry.getTracer("TelemetryContextTest") val span = tracer.spanBuilder(name) for ((key, value) in attributes) { diff --git a/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/OpenTelemetryManager.kt b/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/OpenTelemetryManager.kt index 3f4e98200fa..0317dde3db7 100644 --- a/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/OpenTelemetryManager.kt +++ b/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/OpenTelemetryManager.kt @@ -64,7 +64,7 @@ internal object OpenTelemetryManager { Log.d("OpenTelemetryManager", "mmap file: $mmapFileCurrent") val mutationContext = - CrashlyticsOtelContext.initialize(mmapFileCurrent.absolutePath, MmapSize.SMALL) { + TelemetryContext.initialize(mmapFileCurrent.absolutePath, MmapSize.SMALL) { recoveredSpans -> val recoverPreviousSpans = Firebase.crashlytics.didCrashOnPreviousExecution() if (recoverPreviousSpans) { diff --git a/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/CrashlyticsOtelContext.kt b/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/TelemetryContext.kt similarity index 94% rename from firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/CrashlyticsOtelContext.kt rename to firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/TelemetryContext.kt index 4ebe3d66577..1f2cd0f3715 100644 --- a/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/CrashlyticsOtelContext.kt +++ b/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/TelemetryContext.kt @@ -24,14 +24,15 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch /** - * The companion lifecycle manager for interacting with the crashlytics-otel native library. Exposes - * thread-safe initialization, standard shutdown lifecycles, and returns a synchronized + * The companion lifecycle manager for interacting with the firebase telemetry persistence library + * + * Exposes thread-safe initialization, standard shutdown lifecycles, and returns a synchronized * [MutationContext] for safe span storage operations. */ -internal class CrashlyticsOtelContext private constructor() { +internal class TelemetryContext private constructor() { companion object { - private const val LOG_TAG = "CrashlyticsOtelContext" + private const val LOG_TAG = "TelemetryContext" private val lock = ReentrantLock() private var mutationContext: MutationContext? = null From d2a4ee943b5d80385726271e4a0ef6e166baa193 Mon Sep 17 00:00:00 2001 From: Matthew Robertson Date: Wed, 23 Sep 2026 14:47:32 -0400 Subject: [PATCH 2/4] Format --- .../firebase/crashlytics/telemetry/OpenTelemetryManager.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/OpenTelemetryManager.kt b/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/OpenTelemetryManager.kt index 0317dde3db7..a048119b458 100644 --- a/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/OpenTelemetryManager.kt +++ b/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/OpenTelemetryManager.kt @@ -64,8 +64,7 @@ internal object OpenTelemetryManager { Log.d("OpenTelemetryManager", "mmap file: $mmapFileCurrent") val mutationContext = - TelemetryContext.initialize(mmapFileCurrent.absolutePath, MmapSize.SMALL) { - recoveredSpans -> + TelemetryContext.initialize(mmapFileCurrent.absolutePath, MmapSize.SMALL) { recoveredSpans -> val recoverPreviousSpans = Firebase.crashlytics.didCrashOnPreviousExecution() if (recoverPreviousSpans) { Log.d("OpenTelemetryManager", "Found ${recoveredSpans.size} recovered spans to export.") From 8b877b0c4210c3905aefb169dbec0cb46be70c07 Mon Sep 17 00:00:00 2001 From: Matthew Robertson Date: Thu, 24 Sep 2026 15:47:10 -0400 Subject: [PATCH 3/4] Rename to PersistenceContext --- ...ntextTest.kt => PersistenceContextTest.kt} | 110 +++++++++--------- .../telemetry/OpenTelemetryManager.kt | 3 +- ...emetryContext.kt => PersistenceContext.kt} | 2 +- 3 files changed, 58 insertions(+), 57 deletions(-) rename firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/{TelemetryContextTest.kt => PersistenceContextTest.kt} (86%) rename firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/{TelemetryContext.kt => PersistenceContext.kt} (98%) diff --git a/firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/TelemetryContextTest.kt b/firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/PersistenceContextTest.kt similarity index 86% rename from firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/TelemetryContextTest.kt rename to firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/PersistenceContextTest.kt index e3f304597e2..9277f5d3d02 100644 --- a/firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/TelemetryContextTest.kt +++ b/firebase-crashlytics-telemetry/src/androidTest/java/com/google/firebase/crashlytics/telemetry/PersistenceContextTest.kt @@ -37,7 +37,7 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) -class TelemetryContextTest { +class PersistenceContextTest { @get:Rule val otelRule: OpenTelemetryRule = OpenTelemetryRule.create() private lateinit var testMmapFile: File @@ -49,24 +49,24 @@ class TelemetryContextTest { testMmapFile = File(targetContext.cacheDir, "shared_test_context.mmap") if (testMmapFile.exists()) testMmapFile.delete() - mutationContext = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) + mutationContext = PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) Span.getInvalid().makeCurrent() } @After fun tearDown() { - TelemetryContext.shutdown() + PersistenceContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() } - // --- 1. TelemetryContext.initialize --- + // --- 1. PersistenceContext.initialize --- @Test fun initialize_createsMmapFile() { - TelemetryContext.shutdown() + PersistenceContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() - TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) + PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) assertThat(testMmapFile.exists()).isTrue() } @@ -79,10 +79,10 @@ class TelemetryContextTest { fun initialize_withOnRecovery_returnsRecoveredSpans() { val originalSpan = createTestSpan("RecoverableSpan") mutationContext.addSpan(originalSpan) - TelemetryContext.shutdown() + PersistenceContext.shutdown() val latch = CountDownLatch(1) - TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recoveredSpans -> + PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recoveredSpans -> assertThat(recoveredSpans.size).isEqualTo(1) latch.countDown() } @@ -93,7 +93,7 @@ class TelemetryContextTest { fun initialize_withCorruptedNonTerminatedSpanName_recoversSafelyWithoutCrash() { val originalSpan = createTestSpan("InitialSpan") mutationContext.addSpan(originalSpan) - TelemetryContext.shutdown() + PersistenceContext.shutdown() // Write 64 non-null bytes into RawSpan.name (offset 88) to test create_bounded_jstring handling // of non-terminated/oversized buffer @@ -104,7 +104,7 @@ class TelemetryContextTest { val latch = CountDownLatch(1) var recoveredSpans: List? = null - TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> recoveredSpans = recovered latch.countDown() } @@ -118,16 +118,16 @@ class TelemetryContextTest { @Test fun initialize_calledTwice_returnsSingleton() { - val secondContext = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) + val secondContext = PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) assertThat(secondContext).isSameInstanceAs(mutationContext) } @Test fun initialize_afterShutdown_createsNewInstance() { val firstContext = mutationContext - TelemetryContext.shutdown() + PersistenceContext.shutdown() - val secondContext = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) + val secondContext = PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) assertThat(secondContext).isNotSameInstanceAs(firstContext) } @@ -137,16 +137,16 @@ class TelemetryContextTest { val threadPool = Executors.newFixedThreadPool(threads) val countDownLatch = CountDownLatch(threads + 1) - TelemetryContext.shutdown() + PersistenceContext.shutdown() val targetContext = InstrumentationRegistry.getInstrumentation().targetContext val recoveryMmapFile = File(targetContext.cacheDir, "concurrent_recovery.mmap") if (recoveryMmapFile.exists()) recoveryMmapFile.delete() val prePopulateContext = - TelemetryContext.initialize(recoveryMmapFile.absolutePath, MmapSize.SMALL) + PersistenceContext.initialize(recoveryMmapFile.absolutePath, MmapSize.SMALL) prePopulateContext.addSpan(createThreadSafeTestSpan(1, 1)) - TelemetryContext.shutdown() + PersistenceContext.shutdown() val recoveryInvocationCount = AtomicInteger(0) val onRecovery: suspend (List) -> Unit = { @@ -163,7 +163,7 @@ class TelemetryContextTest { for (i in 0 until threads) { threadPool.submit { try { - TelemetryContext.initialize( + PersistenceContext.initialize( recoveryMmapFile.absolutePath, MmapSize.SMALL, onRecovery, @@ -178,29 +178,29 @@ class TelemetryContextTest { assertThat(recoveryInvocationCount.get()).isEqualTo(1) } finally { threadPool.shutdown() - TelemetryContext.shutdown() + PersistenceContext.shutdown() if (recoveryMmapFile.exists()) recoveryMmapFile.delete() } } - // --- 2. TelemetryContext.shutdown --- + // --- 2. PersistenceContext.shutdown --- @Test fun shutdown_beforeInitialize_doesNotThrow() { - TelemetryContext.shutdown() // Tear down default - TelemetryContext.shutdown() // Should run safely when already null + PersistenceContext.shutdown() // Tear down default + PersistenceContext.shutdown() // Should run safely when already null } @Test fun shutdown_clearsMutationContext() { - TelemetryContext.shutdown() + PersistenceContext.shutdown() assertThat(mutationContext.countSpans()).isEqualTo(0L) } @Test fun shutdown_calledTwice_doesNotThrow() { - TelemetryContext.shutdown() - TelemetryContext.shutdown() + PersistenceContext.shutdown() + PersistenceContext.shutdown() } @Test @@ -213,7 +213,7 @@ class TelemetryContextTest { for (i in 0 until threads) { threadPool.submit { try { - TelemetryContext.shutdown() + PersistenceContext.shutdown() } finally { countDownLatch.countDown() } @@ -227,10 +227,10 @@ class TelemetryContextTest { @Test fun shutdown_calledConcurrentlyWithMutations_doesNotCrashOrThrow() { - TelemetryContext.shutdown() + PersistenceContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() - val context = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) + val context = PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) val threads = 10 val iterations = 50 @@ -259,7 +259,7 @@ class TelemetryContextTest { startLatch.countDown() Thread.sleep(5) - TelemetryContext.shutdown() + PersistenceContext.shutdown() assertThat(countDownLatch.await(5, TimeUnit.SECONDS)).isTrue() } finally { @@ -283,7 +283,7 @@ class TelemetryContextTest { @Test fun countSpans_afterShutdown_returnsZero() { - TelemetryContext.shutdown() + PersistenceContext.shutdown() assertThat(mutationContext.countSpans()).isEqualTo(0L) } @@ -300,9 +300,9 @@ class TelemetryContextTest { val originalSpan = createTestSpan("PersistedSpan", mapOf("k" to "v")) mutationContext.addSpan(originalSpan) - TelemetryContext.shutdown() + PersistenceContext.shutdown() - TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> assertThat(recovered).isNotEmpty() } } @@ -313,11 +313,11 @@ class TelemetryContextTest { val span = createTestSpan(oversizedName) mutationContext.addSpan(span) - TelemetryContext.shutdown() + PersistenceContext.shutdown() val latch = CountDownLatch(1) var recoveredSpans: List? = null - TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> recoveredSpans = recovered latch.countDown() } @@ -336,11 +336,11 @@ class TelemetryContextTest { val span = createTestSpan("OversizedAttrSpan", mapOf(oversizedKey to oversizedValue)) mutationContext.addSpan(span) - TelemetryContext.shutdown() + PersistenceContext.shutdown() val latch = CountDownLatch(1) var recoveredSpans: List? = null - TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> recoveredSpans = recovered latch.countDown() } @@ -364,11 +364,11 @@ class TelemetryContextTest { val span = createTestSpan(utf8Name, mapOf(utf8Key to utf8Value)) mutationContext.addSpan(span) - TelemetryContext.shutdown() + PersistenceContext.shutdown() val latch = CountDownLatch(1) var recoveredSpans: List? = null - TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> recoveredSpans = recovered latch.countDown() } @@ -385,11 +385,11 @@ class TelemetryContextTest { val span = createTestSpan("", emptyMap()) mutationContext.addSpan(span) - TelemetryContext.shutdown() + PersistenceContext.shutdown() val latch = CountDownLatch(1) var recoveredSpans: List? = null - TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> recoveredSpans = recovered latch.countDown() } @@ -403,16 +403,16 @@ class TelemetryContextTest { @Test fun addSpan_afterShutdown_isNoOp() { - TelemetryContext.shutdown() + PersistenceContext.shutdown() mutationContext.addSpan(createTestSpan("AfterShutdown")) } @Test fun addSpan_calledConcurrently_maintainsAccurateCount() { - TelemetryContext.shutdown() + PersistenceContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() - val context = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) + val context = PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) val threads = 10 val iterations = 20 @@ -458,16 +458,16 @@ class TelemetryContextTest { @Test fun endSpan_afterShutdown_isNoOp() { - TelemetryContext.shutdown() + PersistenceContext.shutdown() mutationContext.endSpan("0000000000000000") } @Test fun endSpan_calledConcurrently_maintainsConsistentState() { - TelemetryContext.shutdown() + PersistenceContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() - val context = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) + val context = PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) val threads = 10 val iterations = 20 @@ -553,11 +553,11 @@ class TelemetryContextTest { val oversizedValue = "v".repeat(300) mutationContext.setAttributeOnSpan(span.spanId, oversizedKey, oversizedValue) - TelemetryContext.shutdown() + PersistenceContext.shutdown() val latch = CountDownLatch(1) var recoveredSpans: List? = null - TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> + PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.SMALL) { recovered -> recoveredSpans = recovered latch.countDown() } @@ -579,16 +579,16 @@ class TelemetryContextTest { @Test fun setAttributeOnSpan_afterShutdown_isNoOp() { - TelemetryContext.shutdown() + PersistenceContext.shutdown() mutationContext.setAttributeOnSpan("0000000000000000", "key", "value") } @Test fun setAttributeOnSpan_calledConcurrently_doesNotCrashOrCorrupt() { - TelemetryContext.shutdown() + PersistenceContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() - val context = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) + val context = PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) val threads = 10 val iterations = 20 @@ -632,10 +632,10 @@ class TelemetryContextTest { @Test fun mixedOperations_calledConcurrently_maintainsDataIntegrityAndRecoversCleanly() { - TelemetryContext.shutdown() + PersistenceContext.shutdown() if (testMmapFile.exists()) testMmapFile.delete() - val context = TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) + val context = PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) val threads = 10 val iterations = 20 @@ -661,11 +661,11 @@ class TelemetryContextTest { assertThat(countDownLatch.await(10, TimeUnit.SECONDS)).isTrue() assertThat(context.countSpans()).isEqualTo((threads * iterations).toLong()) - TelemetryContext.shutdown() + PersistenceContext.shutdown() val recoveryLatch = CountDownLatch(1) var recoveredSpans: List? = null - TelemetryContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) { recovered -> + PersistenceContext.initialize(testMmapFile.absolutePath, MmapSize.LARGE) { recovered -> recoveredSpans = recovered recoveryLatch.countDown() } @@ -684,7 +684,7 @@ class TelemetryContextTest { name: String, attributes: Map = emptyMap(), ): CrashlyticsSpan { - val tracer = otelRule.openTelemetry.getTracer("TelemetryContextTest") + val tracer = otelRule.openTelemetry.getTracer("PersistenceContextTest") val span = tracer.spanBuilder(name) for ((key, value) in attributes) { diff --git a/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/OpenTelemetryManager.kt b/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/OpenTelemetryManager.kt index a048119b458..094f4d37f18 100644 --- a/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/OpenTelemetryManager.kt +++ b/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/OpenTelemetryManager.kt @@ -64,7 +64,8 @@ internal object OpenTelemetryManager { Log.d("OpenTelemetryManager", "mmap file: $mmapFileCurrent") val mutationContext = - TelemetryContext.initialize(mmapFileCurrent.absolutePath, MmapSize.SMALL) { recoveredSpans -> + PersistenceContext.initialize(mmapFileCurrent.absolutePath, MmapSize.SMALL) { recoveredSpans + -> val recoverPreviousSpans = Firebase.crashlytics.didCrashOnPreviousExecution() if (recoverPreviousSpans) { Log.d("OpenTelemetryManager", "Found ${recoveredSpans.size} recovered spans to export.") diff --git a/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/TelemetryContext.kt b/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/PersistenceContext.kt similarity index 98% rename from firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/TelemetryContext.kt rename to firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/PersistenceContext.kt index 1f2cd0f3715..ba5a4490533 100644 --- a/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/TelemetryContext.kt +++ b/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/PersistenceContext.kt @@ -29,7 +29,7 @@ import kotlinx.coroutines.launch * Exposes thread-safe initialization, standard shutdown lifecycles, and returns a synchronized * [MutationContext] for safe span storage operations. */ -internal class TelemetryContext private constructor() { +internal class PersistenceContext private constructor() { companion object { private const val LOG_TAG = "TelemetryContext" From 84ff3f64473d1faab63cccff078d24dc071b8c9f Mon Sep 17 00:00:00 2001 From: Matthew Robertson Date: Thu, 24 Sep 2026 15:48:14 -0400 Subject: [PATCH 4/4] Tag --- .../google/firebase/crashlytics/telemetry/PersistenceContext.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/PersistenceContext.kt b/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/PersistenceContext.kt index ba5a4490533..313fec56f0f 100644 --- a/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/PersistenceContext.kt +++ b/firebase-crashlytics-telemetry/src/main/java/com/google/firebase/crashlytics/telemetry/PersistenceContext.kt @@ -32,7 +32,7 @@ import kotlinx.coroutines.launch internal class PersistenceContext private constructor() { companion object { - private const val LOG_TAG = "TelemetryContext" + private const val LOG_TAG = "PersistenceContext" private val lock = ReentrantLock() private var mutationContext: MutationContext? = null