chore: Release 6.1.5#174
Conversation
| <package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd"> | ||
| <metadata> | ||
| <version>6.1.4</version> | ||
| <version>6.1.5</version> |
There was a problem hiding this comment.
🔴 Android SDK 5.8.0 introduces a new runtime dependency on kotlinx-serialization-json:1.6.3 (declared in the upstream core-5.8.0.pom) used by the new FeatureFlagsJsonParser/FeatureFlagsRefreshService/RemoteFeatureFlagsResult classes shipped in this PR's core-release.aar, but neither the nuspec (lines 22-39) nor OneSignalSDK.DotNet.Android.csproj declares an equivalent Xamarin.KotlinX.Serialization.Json package. Since Xamarin AAR transitive deps are not auto-resolved (the existing Dagger comment in the csproj documents exactly this caveat), consumers of 6.1.5 will hit java.lang.NoClassDefFoundError: kotlinx.serialization.json.Json when the new remote feature-flag fetch path runs during SDK init. Fix: add Xamarin.KotlinX.Serialization.Json (matching upstream version 1.6.3) to the net10.0-android21.0 group in the nuspec and as a PackageReference in OneSignalSDK.DotNet.Android.csproj.
Extended reasoning...
What the bug is
OneSignal Android SDK 5.8.0 introduces a new feature-flag refresh subsystem (upstream PRs SDK-4176 / SDK-4210 / SDK-4363) that uses Kotlin serialization to parse remote JSON responses. The new FeatureFlagsJsonParser class in the bundled core-release.aar declares a private static final kotlinx.serialization.json.Json format field and uses the Json { … } DSL (JsonBuilder.setIgnoreUnknownKeys, setLenient, etc.), plus JsonObject/JsonElement/JsonPrimitive/JsonArray types. None of these classes are bundled inside the AAR's classes.jar — only com/onesignal/* is present.
The corresponding upstream Maven POM (com.onesignal:core:5.8.0) declares org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3 as a runtime dependency, which is how the OneSignal Android SDK consumers normally pick it up. But Xamarin/.NET binding projects do not auto-resolve Maven transitive dependencies for <LibraryProjectZip>-style AAR consumption — the existing comment on the Xamarin.Google.Dagger line in OneSignalSDK.DotNet.Android.csproj documents this exact limitation (xamarin/XamarinComponents#1069). Each native runtime dependency must be mirrored as an explicit Xamarin.* NuGet package.
Why the existing dependencies don't cover it
The net10.0-android21.0 dependency group in OneSignalSDK.DotNet.nuspec (lines 22-39) declares only Xamarin.Kotlin.StdLib.Jdk8, Xamarin.KotlinX.Coroutines.Core/Android, the AndroidX.* set, Firebase.Messaging, Google.Dagger, and GooglePlayServices.Base. None of these transitively pulls in kotlinx-serialization-json — it is a separate artifact (Xamarin.KotlinX.Serialization.Json on NuGet). A repo-wide grep for Serialization returns only an iOS file. The same omission holds for OneSignalSDK.DotNet.Android.csproj and OneSignalSDK.DotNet.Android.Core.Binding.csproj.
The previous 5.7.7 AAR did not reference any kotlinx/serialization/json/* types, so the dependency was correctly absent before — this is genuinely a 5.8.0 regression introduced by this PR. The pattern of mirroring native dep changes alongside AAR bumps is established (PR #171 / 6.1.4 bumped Xamarin.Kotlin.StdLib.Jdk8 from 1.8.0.1 to 1.9.23.3 alongside its AAR bump).
Why CI doesn't catch it
The release workflow in .github/workflows/create-release-pr.yml runs only dotnet build. The .NET compiler validates against the AAR's class metadata — it doesn't load the missing kotlinx.serialization.json.* classes at compile time, and the build will succeed cleanly. The error only surfaces at runtime on a real Android device when the JVM classloader tries to resolve Lkotlinx/serialization/json/Json; and fails.
Step-by-step proof
- App developer installs
OneSignalSDK.DotNet 6.1.5from NuGet. - NuGet resolves the
net10.0-android21.0dependency group and pulls in the eight packages declared in the nuspec — none of which isXamarin.KotlinX.Serialization.Json. - App calls
OneSignal.Initialize(...)on Android. - The 5.8.0 init path schedules a remote feature-flag fetch (per upstream PR #2612 "Turbine remote SDK feature flags and foreground refresh").
FeatureFlagsRefreshServiceinvokesFeatureFlagsJsonParser.parseSuccessful(...).- JVM eagerly resolves the
formatstatic field's type:Lkotlinx/serialization/json/Json;. - Classloader fails to find the class in any DEX in the APK (because the package author never declared the dependency, so it was never packaged).
java.lang.NoClassDefFoundError: kotlinx.serialization.json.Jsonis thrown, propagating up and breaking the SDK init / feature-flag path on every consumer device.
How to fix
Add to the net10.0-android21.0 group of OneSignalSDK.DotNet.nuspec and to OneSignalSDK.DotNet.Android/OneSignalSDK.DotNet.Android.csproj (matching the existing Coroutines/StdLib pattern):
<dependency id="Xamarin.KotlinX.Serialization.Json" version="1.6.3.x" />(pick the latest Xamarin.KotlinX.Serialization.Json NuGet that wraps kotlinx-serialization-json:1.6.3, matching the upstream POM).
| @@ -1,4 +1,4 @@ | |||
| { | |||
| "android": "5.7.7", | |||
| "android": "5.8.0", | |||
There was a problem hiding this comment.
🟣 Android SDK 5.8.0's core POM declares com.onesignal:otel:5.8.0 as a runtime dependency, but the .NET binding does not bundle this AAR — update_native_binaries.sh only fetches core/notifications/location/in-app-messages, and there is no OneSignalSDK.DotNet.Android.Otel.Binding project. The bundled core-release.aar holds direct symbolic references to ~11 com.onesignal.otel.* types from classes wired into SDK init (OtelLifecycleManager, OneSignalCrashUploaderWrapper, OneSignalCrashHandlerFactory, AndroidOtelLogger, OtelAnrDetector), so the new 5.8.0 crash-uploader / ANR / OpenTelemetry features will be non-functional and will surface NoClashDefFoundError whenever those code paths are exercised. Pre-existing — the otel split was introduced in 5.7.0 and 6.1.4 already shipped with the same gap, but bumping to 5.8.0 expands the otel surface area, so this release is a natural opportunity to add an Otel.Binding project + curl line + nuspec entry.
Extended reasoning...
What is broken
Starting with Android SDK 5.7.0, OneSignal split its OpenTelemetry / crash-uploader code into a separate Maven artifact, com.onesignal:otel. The com.onesignal:core:5.8.0 POM (https://repo1.maven.org/maven2/com/onesignal/core/5.8.0/core-5.8.0.pom) declares com.onesignal:otel:5.8.0 as a runtime <dependency>. Inside core-release.aar, ~11 distinct com.onesignal.otel.* symbols (IOtelLogger, IOtelCrashHandler, IOtelOpenTelemetryRemote, IOtelPlatformProvider, OtelFactory, crash/IOtelAnrDetector, crash/OtelCrashUploader, etc.) are referenced as method parameters, return types, and field types from compiled classes such as OtelLifecycleManager, OneSignalCrashUploaderWrapper, OneSignalCrashHandlerFactory, AndroidOtelLogger, and OtelAnrDetector. None of those com/onesignal/otel/* classes live anywhere inside the four AARs shipped in this PR.
Why the .NET binding is affected
update_native_binaries.sh (lines 61–78) only curls four artifacts: core, notifications, location, in-app-messages. There is no OneSignalSDK.DotNet.Android.Otel.Binding project, no otel AAR under any Jars/ directory, and no Xamarin/NuGet dependency in the nuspec that would supply the com.onesignal.otel.* package transitively. The bundled AndroidManifest.xml even contains tools:overrideLibrary="com.onesignal.otel", which only makes sense if the otel module is supposed to be linked alongside core.
Step-by-step proof of the runtime crash
- Consumer app initializes OneSignal:
OneSignal.Default.Initialize(appId). - .NET binding calls into Java
com.onesignal.OneSignal, which invokesOneSignalImp.initEssentials(Context). OneSignalImp.initEssentialsbytecodenews anOtelLifecycleManagerand callsinitializeFromCachedConfig()(verified viajavapofOneSignalImp.class, constants #452–#463).OtelLifecycleManagerhas fields/methods typed ascom.onesignal.otel.IOtelCrashHandler,com.onesignal.otel.IOtelOpenTelemetryRemote,com.onesignal.otel.crash.IOtelAnrDetector— first execution of these methods forces the Android verifier/class-loader to resolvecom/onesignal/otel/IOtelCrashHandler, which is not present, throwingNoClassDefFoundError.- Independently,
OneSignalCrashUploaderWrapperis registered as anIStartableServicein the core module and runs at SDK init. On API ≥ 26 (OtelSdkSupport.isSupported) it invokesgetUploader(), whose return type iscom.onesignal.otel.crash.OtelCrashUploader— sameNoClassDefFoundErrorstory.
Addressing the refutation
The refuter argued (a) duplicate of bug_004, (b) pre-existing in 5.7.x without consumer reports, (c) defensively gated behind a feature flag, and therefore not actionable. Points (a) and (b) are accepted — the synthesis description acknowledges this is pre-existing, which is exactly why this report is filed at pre_existing severity rather than normal. On (c): OneSignalCrashUploaderWrapper.start() does have a Throwable catch around its lambda (so that path silently logs "Crash uploader failed to start"), and OtelLifecycleManager.initializeFromCachedConfig will short-circuit when the remote feature flag is off — which is presumably why widespread crash reports have not surfaced. But that defensive behavior depends on (i) Android's lazy class verification not eagerly resolving the missing types and (ii) OneSignal's server keeping the SDK-4363 telemetry flag off for .NET clients. As soon as the flag flips on (or a future version inlines an otel call into a path without a Throwable catch), every .NET consumer will start logging NoClassDefFoundError and lose the advertised 5.8.0 ANR / OpenTelemetry / crash-uploader functionality outright.
How to fix
Add a new OneSignalSDK.DotNet.Android.Otel.Binding project mirroring the existing four binding projects, add a curl line in update_native_binaries.sh for https://repo1.maven.org/maven2/com/onesignal/otel/$ANDROID_VERSION/otel-$ANDROID_VERSION.aar, add the AAR + binding DLL/PDB to OneSignalSDK.DotNet.nuspec, and reference the binding from OneSignalSDK.DotNet.csproj. Since the otel artifact has its own transitive deps (OpenTelemetry-android, etc.), those should also be declared as nuspec dependencies. This release is a natural place to land that work, but if the team prefers to do it in a follow-up, that is reasonable given the long-standing lack of consumer reports.
Why this is filed as pre_existing rather than normal: the gap was introduced in Android SDK 5.7.0 and has shipped in every 6.x .NET release since. This PR does not introduce it; it merely inherits it. The classification follows the synthesis agent's own characterization.
Channels: Current
🛠️ Native Dependency Updates