perf(instrumentation): Inject manifest metadata at build time - #1401
perf(instrumentation): Inject manifest metadata at build time#1401romtsn wants to merge 6 commits into
Conversation
Parse resolved Sentry manifest metadata and inject it into compatible Android SDK versions so startup avoids PackageManager and Bundle unparceling. Fall back to runtime lookup when values cannot be resolved safely. Refs JAVA-531 Co-Authored-By: Codex <noreply@openai.com>
Refs JAVA-531 Co-Authored-By: Codex <noreply@openai.com>
Refs JAVA-531 Co-Authored-By: Codex <noreply@openai.com>
Use android:resource so newer AGP versions cannot resolve the fixture before the merged manifest reaches the parser. Refs JAVA-531 Co-Authored-By: OpenAI Codex <noreply@openai.com>
Prevent AGP dependency instrumentation from reusing manifest metadata injected for another app while keeping older SDK transforms cacheable. Refs JAVA-531 Co-Authored-By: OpenAI Codex <noreply@openai.com>
Generate one manifest metadata cache key from the per-build modules service so repeated Gradle input queries stay stable and configuration-cache restores receive a fresh key. Refs JAVA-531 Co-Authored-By: OpenAI Codex <noreply@openai.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0d32407. Configure here.
| ) | ||
| params.buildTimeMetadataCacheKey.setDisallowChanges( | ||
| params.buildTimeMetadataEnabled.map { | ||
| if (it) runtimeModulesService.get().buildTimeMetadataCacheKey else "" |
There was a problem hiding this comment.
Shared cache key reuses injected metadata
High Severity
buildTimeMetadataCacheKey now comes from SentryModulesService, a single registerIfAbsent build service shared by every project and variant. The isolation key is therefore identical across apps and flavors, so AGP can reuse ManifestMetadataReader bytecode injected for a different variant. Product flavors and debug/release often have different io.sentry.* metadata, including DSNs.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 0d32407. Configure here.
| MANIFEST_METADATA_READER_NAME -> | ||
| ManifestMetadataClassVisitor( | ||
| instrumentationContext.apiVersion.get(), | ||
| nextClassVisitor, |
There was a problem hiding this comment.
Bug: The buildTimeMetadata() function is called twice without caching. A transient failure on the second call will crash the build due to an unchecked null.
Severity: MEDIUM
Suggested Fix
Cache the result of the buildTimeMetadata() call within the factory instance. The result from the first invocation in isInstrumentable should be stored and reused in createClassVisitor. This avoids re-reading the file from disk and eliminates the race condition where a transient I/O error could cause a build crash.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location:
plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SentrySdkOptimizationClassVisitorFactory.kt#L56
Potential issue: The `buildTimeMetadata()` function, which reads and parses the merged
manifest file, is called independently in `isInstrumentable` and `createClassVisitor`
without caching the result. The Android Gradle Plugin (AGP) guarantees that if
`isInstrumentable` returns true, `createClassVisitor` will be called. However, if the
first file read succeeds but the second one fails due to a transient I/O error, the
`checkNotNull(buildTimeMetadata())` call in `createClassVisitor` will throw an
`IllegalStateException`, causing the build to crash. This creates a potential for rare,
hard-to-diagnose build failures.
Also affects:
plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SentrySdkOptimizationClassVisitorFactory.kt:69~72
|
Superseded by a generated app-class design. The replacement makes the SDK bytecode rewrite identical for every app and puts variant-specific manifest values in a normally generated app class, avoiding synthetic cache keys and cross-app transform reuse. |


📜 Description
Parse resolved
io.sentry.*entries from the merged Android manifest and injecttheir typed values into
ManifestMetadataReaderduring bytecode instrumentation.The optimization uses the existing
runtimeOptimizations.enabledflag and onlyruns with sentry-android-core 8.54.0 or newer.
Resource references, unresolved placeholders, and unsupported SDK versions keep
using the existing runtime manifest lookup.
Companion SDK PR: getsentry/sentry-java#5963
💡 Motivation and Context
Sentry Android initialization currently retrieves application metadata through
PackageManagerand lazily unparcels itsBundle. Resolving manifest metadata atbuild time removes both operations from the startup path.
Refs JAVA-531
💚 How did you test it?
./gradlew spotlessApplygetApplicationInfocalls disappear when injected📝 Checklist
🔮 Next steps
Merge the SDK support in getsentry/sentry-java#5963 before releasing this optimization.