[Android] Enable TlsContext/TlsSession on Android - #131461
Conversation
Phase 1 of the SslStream/TlsSession unification effort: bring Android onto the real TlsSession implementation so it stops being the only platform that has to throw PlatformNotSupportedException from the new low-level TLS API surface introduced in dotnet#130366. ## Why it was stubbed Pal.Android/SafeDeleteSslContext.cs requires an SslStream.JavaProxy on the SslAuthenticationOptions bag. JavaProxy was constructed with an SslStream instance and its remote-cert-validation callback closed over that specific stream — so the JSSE trust manager back-channel could only route into an SslStream. TlsSession has no SslStream, so constructing SafeDeleteSslContext threw immediately, and TlsSession.cs was shut off for Android via TlsSession.Stub.cs. ## What this change does - Decouple JavaProxy from SslStream. It now carries a `Func<IntPtr, RemoteCertificateValidationResult>` validator that its static UnmanagedCallersOnly trampoline invokes. SslStream keeps a convenience overload (`new JavaProxy(this)`) that closes over SslStream.VerifyRemoteCertificate(IntPtr). TlsSession supplies its own validator (see below). - Add TlsSession.Android.cs. Populates the per-session options bag with a session-owned JavaProxy in a new `InitializePlatformSpecificSessionState` partial method fired from TlsSession.InitializeFromContext. The validator mirrors what SslStream.Android does today: consult the platform trust manager result (opt-in via ShouldRespectPlatformValidation), then run the shared SslStream.VerifyRemoteCertificateCore. Runs synchronously from the JSSE trust manager callback — matching SslStream's behavior on Android, since the JSSE trust manager needs a synchronous decision. - Flip the csproj gating: TlsSession.cs, TlsBufferSession.cs, and TlsSocketSession.cs now compile on Android; TlsSession.Stub.cs is restricted to the netstandard build only. - Move Pal.Android/SafeDeleteSslContext from the `System.Net` namespace to `System.Net.Security` — matching where the base class (SafeDeleteContext) and the Windows/Linux SafeDeleteSslContext already live. Purely a naming fix; the type is internal so nothing outside the assembly cares. Lets us drop the ugly `#elif TARGET_ANDROID` special case from TlsSession.cs's TlsSecurityContext alias block. - Fix a signature inconsistency uncovered while wiring TlsSession's ref-passing through the Android PAL: SslStreamPal.Android.AcceptSecurityContext / InitializeSecurityContext took `ref SafeFreeCredentials credential` (non-nullable), unlike Windows/Linux/OSX which take `ref SafeFreeCredentials?`. Android's AcquireCredentialsHandle always returns null and HandshakeInternal never reads the value, so changing to nullable is a no-op behaviorally and simply aligns the four PAL signatures. SslStream's existing `_credentialsHandle!` call sites still compile unchanged. - Enable TlsSessionTests on Android in the test csproj so the existing coverage runs in Android CI. ## Not included - No changes to the SslStream code path — SslStream continues to drive its own PAL glue as before. This PR only removes the "Android is stubbed" asterisk on the TlsSession API. - Pal.OSX/SafeDeleteSslContext is still under `System.Net` (same historical inconsistency as the Android one). Not touched here because macOS's TlsSession alias already uses the base class (SafeDeleteContext, correctly namespaced) — the macOS mismatch is cosmetic-only and can move to a separate cleanup PR. ## Sequencing Follows dotnet#130366 (merged) and dotnet#131457 (small follow-up). Precedes the SslStream → TlsSession adapter work (Phase 2/3 in the plan we sketched), which needs TlsSession to be universally supported before SslStream can be a thin shim over it.
Follow-up to enabling TlsContext/TlsSession on Android in the previous commit: remove the blanket Android exclusion of System.Net.Security.Tests.csproj so the new TlsSession Android implementation actually gets covered by CI, along with the existing SslStream tests that already had Android-specific handling (SkipOnPlatform, ActiveIssue attributes, OperatingSystem.IsAndroid() guards throughout the test source). Historical context: - The exclusion originated pre-dotnet#68020 (2022). PR dotnet#68020 removed a blanket exclusion and added per-test ActiveIssue markers, but the exclusion later reappeared unconditionally on both TargetOS=android and TargetsLinuxBionic. - A separate x64/LinuxBionic-only exclusion (the one that documents "Timeout on Helix, cannot repro locally") remains in place for now — if x64 emulators still time out we can narrow the re-enablement. If Android CI reports emulator timeouts on this PR, we'll narrow the re-enablement (e.g., split TlsSessionTests into a standalone Android-only project along the lines of the existing AndroidPlatformTrustTests, or keep the exclusion for x64 but not arm64).
|
Triggering Android CI to validate the new TlsSession Android implementation: /azp run runtime-extra-platforms Note This comment was posted with GitHub Copilot assistance. |
|
Azure Pipelines: Successfully started running 4 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones |
There was a problem hiding this comment.
Pull request overview
This PR enables the real TlsSession/TlsContext implementation on Android by decoupling Android’s JSSE bridge (SslStream.JavaProxy) from SslStream, wiring a session-owned proxy for TlsSession, and updating project file gating so the non-stub TLS session code compiles for Android and is exercised by the Android CI test matrix.
Changes:
- Refactored
SslStream.JavaProxyto be delegate-based so bothSslStreamandTlsSessioncan own/use it on Android. - Added an Android-specific
TlsSessionpartial to attach the proxy and perform synchronous certificate validation through the existing shared validation core. - Updated build/test project gating to compile
TlsSessionon Android and to stop excludingSystem.Net.Securityfunctional tests from Android CI.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/tests.proj | Removes the exclusion that prevented System.Net.Security functional tests from running on Android CI. |
| src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj | Includes TlsSessionTests.cs for Android builds. |
| src/libraries/System.Net.Security/src/System/Net/Security/TlsSession.cs | Adds a platform hook to allow platform-specific per-session initialization (Android proxy wiring). |
| src/libraries/System.Net.Security/src/System/Net/Security/TlsSession.Android.cs | New Android partial that attaches a session-owned JavaProxy and routes JSSE trust-manager validation into VerifyRemoteCertificateCore. |
| src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs | Aligns PAL signatures with other platforms by using nullable SafeFreeCredentials?. |
| src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs | Refactors JavaProxy to hold a validator delegate instead of a hard SslStream reference. |
| src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs | Moves SafeDeleteSslContext into the System.Net.Security namespace for consistency with other platforms. |
| src/libraries/System.Net.Security/src/System.Net.Security.csproj | Enables real TlsSession compilation on Android, gates the stub to netstandard-only, and adds TlsSession.Android.cs under UseAndroidCrypto. |
| <Compile Include="TlsSessionTests.cs" | ||
| Condition="'$(TargetPlatformIdentifier)' == 'unix' or '$(TargetPlatformIdentifier)' == 'windows' or '$(TargetPlatformIdentifier)' == 'osx'" /> | ||
| Condition="'$(TargetPlatformIdentifier)' == 'unix' or '$(TargetPlatformIdentifier)' == 'windows' or '$(TargetPlatformIdentifier)' == 'osx' or '$(TargetPlatformIdentifier)' == 'android'" /> |
| // Wires a session-owned JavaProxy onto the per-session options bag so | ||
| // Pal.Android.SafeDeleteSslContext can look it up during construction. The proxy | ||
| // delegates back to VerifyRemoteCertificateForAndroid on this session — mirroring | ||
| // the model SslStream uses via SslStream.Android.VerifyRemoteCertificate(IntPtr), | ||
| // but keeping the JSSE bridge scoped to the session that created it. |
Phase 1 of the SslStream → TlsSession unification effort. Bring Android onto the real
TlsSessionimplementation so it stops being the only platform stuck onTlsSession.Stub.cs(throwingPlatformNotSupportedException), and re-enable theSystem.Net.Securityfunctional test suite on Android CI to actually exercise the new code path.Background
TlsContext/TlsSessionshipped as experimental API in #130366. On every platform except Android, the real implementation compiles and works (macOS/iOS/tvOS via SecureTransport; Linux/FreeBSD via OpenSSL; Windows via SChannel). Android was stubbed becausePal.Android/SafeDeleteSslContextrequiredSslAuthenticationOptions.SslStreamProxy, and theSslStream.JavaProxythat populated it was hardwired to close over anSslStreaminstance for its JSSE trust-manager back-channel.TlsSessionhas noSslStream, soSafeDeleteSslContext(options)would throw immediately, and the source files were shut off entirely for Android viaTlsSession.Stub.cs.Changes
JavaProxyfromSslStream(SslStream.Android.cs): the proxy now carries aFunc<IntPtr, RemoteCertificateValidationResult>validator.SslStreamkeeps a convenience overloadnew JavaProxy(this)that closes over its privateVerifyRemoteCertificate(IntPtr)method, soSslStreambehavior is bit-for-bit identical.TlsSession.Android.cs: populates each per-session options bag with a session-ownedJavaProxywhose validator mirrorsSslStream.Android's trust-manager routing (ShouldRespectPlatformValidation+VerifyRemoteCertificateCore). Runs synchronously from the JSSE trust-manager callback, since JSSE needs a synchronous decision — same constraint that shapes SslStream on Android today.TlsSession.cs: added apartial void InitializePlatformSpecificSessionState()hook fired fromInitializeFromContextso per-platform partials (currently just the Android one) can wire session-local native bridge state without polluting the shared file.System.Net.Security.csprojgating: realTlsSession.cs/TlsBufferSession.cs/TlsSocketSession.csnow compile on Android;TlsSession.Stub.csis restricted to the "no target platform" netstandard build only; newTlsSession.Android.csconditioned onUseAndroidCrypto.Pal.Android/SafeDeleteSslContextmoved toSystem.Net.Securitynamespace to match Windows/Linux (and the baseSafeDeleteContexttype). Purely a rename; the type isinternal sealed. Lets us drop the#elif TARGET_ANDROIDspecial case fromTlsSession.cs'sTlsSecurityContextalias block. The equivalent macOS mismatch (Pal.OSX/SafeDeleteSslContextinSystem.Net) is untouched — macOS's TlsSession uses the base class already.SslStreamPal.Android.cssignature cleanup:AcceptSecurityContext/InitializeSecurityContexttookref SafeFreeCredentials credential(non-nullable) instead of theref SafeFreeCredentials?used on Windows/Linux/OSX. Android'sAcquireCredentialsHandlealways returns null andHandshakeInternalnever reads the parameter, so this is a no-op behaviourally and just aligns the four PAL signatures.TlsSessionTests.cson Android inSystem.Net.Security.Tests.csproj, and un-excludedSystem.Net.Security.Testsfrom Android CI intests.projso the coverage actually runs.Net diff: ~140 added / ~14 removed across 8 files (excluding tests.proj).
Coordination with #130755
@simonrozsival's #130755 ([Android] Support delayed client certificate selection in SslStream) is currently open and touches the same
JavaProxytype. It changes_handlefromGCHandle?→GCHandle<JavaProxy>, adds a second UnmanagedCallersOnly callback (SelectClientCertificate), renamesRegisterRemoteCertificateValidationCallback()→RegisterCallbacks(), and swaps the Android interop entry point. There will be a real merge conflict onSslStream.Android.csno matter which lands first.Suggestions:
JavaProxyctor on top of the strongly-typedGCHandle<JavaProxy>shape._handlefield).Either way is fine; happy to coordinate.
Follow-ups (not in this PR)
Pal.OSX/SafeDeleteSslContextstill lives underSystem.Net— cosmetic-only, can move to a separate cleanup PR.System.Net.Security.Tests.csprojon Android CI causes the emulator to time out (which is why it was excluded originally), we'll narrow the scope — either arch-specific or a slim standalone Android test project along the lines of the existingAndroidPlatformTrustTests.SslPlatformContextand consolidating the three per-platform caching layers) is a separate PR.SslStreaminternally driven byTlsBufferSession) is another separate PR after that.Sequencing
Follows #130366 (merged) and #131457 (small doc/assert follow-up, open). Unblocks the SslStream → TlsSession adapter work — that adapter can't ship until
TlsSessionis universally supported.Note
This PR description was drafted with GitHub Copilot assistance.