Repository navigation
fix(maui): .NET 10 Android Release build fixes (relocate okio/okhttp) - #791
Merged
Merged
Conversation
…ndled okio/okhttp - Bump OpenTelemetry Java 1.51.0 -> 1.66.0 and OpenTelemetry Android 0.11.0-alpha -> 1.7.0 in observability-android and the MAUI LDObserve native module; migrate to the split SessionProvider/SessionPublisher API, RumBuilder, and the new AttributeType.VALUE / ValueType.EMPTY cases. - Pin kotlinx-coroutines, kotlin-stdlib, androidx.core and okhttp to versions the current Kotlin 2.0.21 / AGP 8.x toolchain can build against. - Drop the Org.Jetbrains.Kotlinx.KotlinxSerializationJson NuGet dependency. Its JVM package injects a jar from a hardcoded Gradle cache path, which duplicated kotlinx.serialization.json classes in apps using Xamarin.KotlinX.Serialization.Json. - Relocate the okio and okhttp3 classes bundled in ldobserve-otel-bundle.jar under com.launchdarkly.sessionreplay.shaded so they no longer collide with Square.OkIO (R8 'defined multiple times' failure in Release builds, e.g. via Firebase). - Update NativeAndroidDeps.props globs for the renamed OTel Android 1.x artifacts. - Fix ObservabilityBridge for the pageViews -> screenViews rename. Co-authored-by: Cursor <cursoragent@cursor.com>
- Add MauiSample10.csproj (net10.0-android/ios) sharing sources with MauiSample9, with isolated obj/bin paths. - Reference Xamarin.Firebase.Messaging/Analytics on Android as a standing check against the Square.OkIO / KotlinX duplicate-class conflicts (exercised by Release builds). - Guard CommunityToolkit.Maui Popup API differences (v11 vs v15) in DialogsPage. - Show platform, .NET runtime version and Debug/Release in the shell title. - Remove global.json so the .NET 10 SDK can build the tree. Co-authored-by: Cursor <cursoragent@cursor.com>
abelonogov-ld
marked this pull request as draft
October 3, 2026 00:02
… consumer apps 0.11.1 raised Xamarin.AndroidX.Lifecycle.* floors to 2.11.0.1, which requires SavedState >= 1.5.0.1 and Collection.Jvm >= 1.6.0.1 and conflicts with our SavedState 1.3.1.1 / Collection 1.5.0.3 floors, producing NU1605 downgrade errors in MAUI apps (e.g. MauiPlanets on MAUI 9). Our Java only uses lifecycle APIs present in 2.9 (ProcessLifecycleOwner, DefaultLifecycleObserver), so go back to the 2.9.2.1 line MAUI itself ships. Stop suppressing NU1605/NU1608 in MauiSample10 so the sample catches this, and bump the package version to 0.11.2. Co-authored-by: Cursor <cursoragent@cursor.com>
abelonogov-ld
marked this pull request as ready for review
October 6, 2026 02:19
Co-authored-by: Cursor <cursoragent@cursor.com>
The okio/okhttp relocation is what fixes the R8 duplicate-class failure in Release, and it is independent of the OTel version: 0.10.2, which shipped on OTel 1.51, already bundles 433 unshaded okio/okhttp3 entries including the okio/-DeflaterSinkExtensions that R8 rejects. Upgrading to OTel Android 1.7 pulls androidx.core 1.19, androidx.lifecycle 2.11 and okhttp-android 5.4, which require compileSdk 36-37 and AGP 9.1. That breaks every native Android consumer still on AGP 8, including our own e2e app, and is a migration the Android owners should land on its own schedule. Reverts the version bumps and the observability-android changes that existed only to track the 1.x API. Keeps the relocation, the Lifecycle floors, the .NET 10 sample and the Analytics.screenViews fix. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This was referenced Oct 6, 2026
abelonogov-ld
added a commit
that referenced
this pull request
Oct 6, 2026
* main: fix(maui): .NET 10 Android Release build fixes (relocate okio/okhttp) (#791)
abelonogov-ld
added a commit
that referenced
this pull request
Oct 6, 2026
Stacked on #791. Review that first; this PR's diff is only the API work on top of it. ## Summary Brings the MAUI public API closer to the Flutter observability SDK. Every option below already existed in the native Android and iOS SDKs. MAUI simply didn't expose it, so this is mostly plumbing through the Kotlin and Swift bridges and the binding definitions. ### Observability options - **`CustomHeaders`** — extra headers sent with OTLP export. - **`LogsApiLevel`** — minimum severity for logs sent through `LDObserve.RecordLog`. Uses a new `ObservabilityLogLevel` enum that follows the OpenTelemetry severity numbers (`Trace` = 1 … `Fatal4` = 24), plus `None` to turn logging off. The default is `Info`. - **`ServiceVersion`** now defaults to `null` and resolves to the app's version (`AppInfo.Current.VersionString`) instead of a hardcoded constant. `DefaultServiceVersion` is marked `[Obsolete]`. **Behavior change:** apps that don't set it now report their real version. - **`LDObserve.NativeVersion`** — the version of the underlying native SDK, useful in support requests. ### Session replay options - **Capture:** `SampleRate`, `FrameRate`, `Scale`, `ImageQuality` (defaults 1.0 / 1.0 / 1.0 / 0.3, the same as native). - **Privacy:** `MinimumAlpha` is now passed through on iOS too. - **`LDIgnore()`** view extension, next to the existing `LDMask()` / `LDUnmask()`. On Android, "ignore" maps to mask, because the native Android SDK has no per-view ignore. The README says so. ### Docs The README now covers the new options, capture settings and `NativeVersion`. ## Not in this PR - Type-based privacy rules (`MaskViewTypes`, `UnmaskViewTypes`, `IgnoreViewTypes`) moved to the `feat/maui-privacy-view-types` branch, stacked on this one, so they can be reviewed separately. - `LDReplay.TrackEvaluation` is effectively dead: nothing calls it, there's no native entry point, and evaluations are already recorded as `evaluation` spans by the observability hook. I suggest marking it `[Obsolete]` separately. - Remaining Flutter-parity gaps: init readiness result, `Shutdown()`, `Track` / `TrackScreenView` / `TrackClick`, `WithSpan`, log capture, and the `analytics` / `traces` / `metricsEnabled` toggles. - The Android bridge passes `Analytics(screenViews = false)` because MAUI is meant to report screen views itself, but MAUI never does. So Android currently records no screen views. Worth a follow-up. ## Verification - Built the sample on Android and iOS, and checked the new fields in the generated Android binding and in the rebuilt xcframework. - After #791 dropped the OTel upgrade, rebuilt the Android binding cleanly against the reverted `observability-android`. `CustomHeaders`, `LogsApiLevel` and `SampleRate` are all present in the generated C#. - **Not verified on a device:** `CustomHeaders` arriving on export. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > Expands the MAUI observability SDK so more native settings are configurable from .NET and documented, aligning with the Flutter SDK. > > **Observability:** Adds `CustomHeaders` and `LogsApiLevel` (new `ObservabilityLogLevel` enum, OTel severities plus `None`). `ServiceVersion` is optional and, when unset, resolves to `AppInfo.Current.VersionString` instead of a fixed default (`DefaultServiceVersion` is obsolete). Exposes `LDObserve.NativeVersion` after init. Values are passed through the Android and iOS bridges. > > **Session replay:** Adds capture knobs `SampleRate`, `FrameRate`, `Scale`, and `ImageQuality`, and wires `Privacy.MinimumAlpha` through on iOS. Adds `View.LDIgnore()` (Android falls back to mask). README covers observability options, capture settings, masking timing, and `NativeVersion`. > > Package version bumps to **0.14.1**. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit bada0a0. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A customer (Delta Faucet) cannot build a MAUI app for
net10.0-androidin Release withLaunchDarkly.SessionReplay. R8 rejects two classes as duplicates:okio/-DeflaterSinkExtensionskotlinx.serialization.json.Json$DefaultBoth come from
ldobserve-otel-bundle.jar, the fat JAR we ship inside the package. The app acquires its own copy of the same classes through unrelated bindings —Square.OkIOarrives transitively viaAndroidX.DataStore, which Firebase depends on — and two definitions of a class is a hard failure for R8. It only surfaces in Release, because Debug does not run R8.This is not new to any particular OTel version. The released 0.10.2 bundle already contains 433 unshaded
okio/andokhttp3/entries, including the exact class R8 names.Fix
okio/okhttp are relocated under
com/launchdarkly/sessionreplay/shaded/byBundleOtelJarsTask, using an ASMClassRemapper.They cannot simply be dropped: the OTLP exporters resolve their transport through
ServiceLoader, the onlyHttpSenderProviderwe register is the OkHttp one, and OkHttp's entire I/O layer is Okio. Removing either produces the "No HttpSenderProvider found on classpath" crash at runtime. Relocating makes the collision impossible instead of depending on the app's dependency graph staying clear.The remapper also rewrites string constants, not just bytecode class references. OkHttp builds the path to its public-suffix resource by concatenating the literal
"okhttp3/internal/publicsuffix/"with a simple class name, so a remapper that only moved class references would relocate the resource and leave the lookup pointing at the old path.kotlinx.serialization is already gone from
observability-androidas of #787, which replaced it withorg.json. This PR drops the now-unusedOrg.Jetbrains.Kotlinx.KotlinxSerializationJsonpackage references from the binding and the fat package. That reference was the other half of the duplicate-class failure: its targets file hardcoded a path into$(Home)/.gradle/caches/....Also in this PR
MauiSample10.csproj— a .NET 10 variant of the sample, sharing sources withMauiSample9.csproj.global.jsonis removed so both SDK bands resolve. Not added to the.sln.Firebase.Common→AndroidX.DataStore.Preferences→Square.OkIO.JVM), so it stays as a standing compatibility check rather than a one-off repro.NoWarnfor NU1605/NU1608 removed from the sample, so dependency regressions are visible instead of silent.Analytics(pageViews = false)→Analytics(screenViews = false)in the MAUI Kotlin bridge.main'sObservabilityOptions.Analyticshas hadscreenViewsfor a while; the MAUI bridge was still passingpageViewsand would not compile against it. The MAUI native Gradle build is not in CI, which is why this went unnoticed.ObservabilityPluginregistration, and named both sample projects.Split out: the OTel upgrade
This branch originally also upgraded to OTel Java 1.66 and OTel Android 1.7. That has been moved to
feat/otel-1.7-upgrade.OTel Android 1.7 pulls
androidx.core1.19,androidx.lifecycle2.11 andokhttp-android5.4, all of which declare a minimum compileSdk of 36–37 and a minimum AGP of 9.1. Our e2e app (compileSdk 35, AGP 8.13.2) failscheckComposeDebugAarMetadataon them, and so would every native Android consumer still on AGP 8. TheresolutionStrategypins that make it build locally only apply inside our own Gradle build; they are not published, so consumers see the raw transitives.Since the relocation fix is independent of the OTel version, there is no reason to hold the customer fix behind an AGP 9 migration.
Verification
okio//okhttp3/entries, 433 relocated under the shaded prefix.-DeflaterSinkExtensionsis relocated; thepublicsuffixes.gzresource moves with its database class.META-INF/servicesintact after the merge;HttpSenderProviderstill resolves toOkHttpHttpSenderProvider.MauiSample10builds fornet10.0-androidin Release with Firebase present: R8 passes, signed APK and AAB produced, 0 errors. This is the customer's failing configuration..Ktxsiblings; they are not caused by this package and are unchanged frommain's pins.Not verified
OTLP export through the relocated OkHttp has not been exercised on a device. The SPI registration and the public-suffix resource are checked statically only.
Note
Overview
Fixes .NET 10 Android Release builds that fail R8 duplicate-class checks when
LaunchDarkly.SessionReplayis used alongside common deps (e.g. Firebase → DataStore → OkIO).Android OTel bundle:
BundleOtelJarsTasknow relocatesokioandokhttp3undercom.launchdarkly.sessionreplay.shaded/via ASM (classes, resources, and string constants such as OkHttp’s public-suffix paths). OkHttp/Okio stay in the bundle for OTLPHttpSenderProvider, but no longer collide with app copies.Org.Jetbrains.Kotlinx.KotlinxSerializationJsonis removed from the Android binding and fat package to avoid the second duplicate (kotlinx.serialization.json.Json$Default).MAUI bridge:
ObservabilityOptions.AnalyticsusesscreenViews = false(was obsoletepageViews) so Android navigation isn’t double-counted with MAUI screen reporting.Samples & docs: Adds
MauiSample10.csproj(shared sources with .NET 9), Firebase on the .NET 10 Android sample as a standing Release R8 check,HttpActivityPropagationSupporton both samples, and README updates (.NET 9/10, HTTP tracing, plugin registration, crash reporting). Package version 0.11.3;global.jsonremoved for multi-SDK builds.Reviewed by Cursor Bugbot for commit 43a4eeb. Bugbot is set up for automated code reviews on this repo. Configure here.