[Android] Bridge SocketsHttpHandler to java.net.ProxySelector#128031
Draft
simonrozsival wants to merge 5 commits into
Draft
[Android] Bridge SocketsHttpHandler to java.net.ProxySelector#128031simonrozsival wants to merge 5 commits into
simonrozsival wants to merge 5 commits into
Conversation
Adds an AndroidPlatformProxy : IWebProxy that resolves the system
proxy chain for SocketsHttpHandler on Android by JNI-calling
java.net.ProxySelector.getDefault().select(URI.create(url)).
This brings the Android system proxy (Wi-Fi proxy, MDM-deployed proxy,
PAC scripts, per-network and VPN ProxyInfo) to the managed HTTP stack,
matching the behavior HttpURLConnection / AndroidMessageHandler exhibit
today. Closes the largest compatibility regression apps would face when
flipping the default HttpClientHandler backend from AndroidMessageHandler
to SocketsHttpHandler on net*-android*.
Implementation lives entirely in dotnet/runtime — no dependency on
dotnet/android, no [UnsafeAccessor] into Mono.Android.
Native side (System.Security.Cryptography.Native.Android):
* pal_proxy.{h,c}: AndroidCryptoNative_GetProxyForUrl /
AndroidCryptoNative_FreeProxyResult. Pure C/JNI using the
existing pal_jni.h INIT_LOCALS / RELEASE_LOCALS /
ON_EXCEPTION_PRINT_AND_GOTO macros for leak-safe local-ref
handling on all exception paths.
* pal_jni.{h,c}: cached jclass/jmethodID/jfieldID globals for
java.net.ProxySelector, Proxy, Proxy$Type, InetSocketAddress,
URI, and java.util.List.
* CMakeLists.txt: pal_proxy.c added to NATIVECRYPTO_SOURCES.
Managed side (System.Net.Http):
* AndroidPlatformProxy.Android.cs: IWebProxy implementation that
P/Invokes the PAL, takes the first non-DIRECT entry, and falls
back to direct on any failure. Mirrors MacProxy in shape.
* SystemProxyInfo.Android.cs: env vars first
(HttpEnvironmentProxy), then AndroidPlatformProxy, then
HttpNoProxy. Gated by the FeatureSwitchDefinition
System.Net.Http.UseAndroidSystemProxy (default true).
* Interop.Proxy.cs: [LibraryImport] declarations.
* csproj: new ItemGroup for TargetPlatformIdentifier=android that
replaces SystemProxyInfo.Unix.cs with the Android-specific files.
Credentials note: AndroidPlatformProxy.Credentials is required by the
IWebProxy contract but is never populated by this class — Android's
proxy APIs do not surface credentials. Users authenticate to system
proxies via HttpClientHandler.DefaultProxyCredentials, the same as on
macOS (MacProxy has the same limitation, tracked as dotnet#24799).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* IsBypassed: always return false (HttpWindowsProxy:349–360 pattern). The previous double-call-into-GetProxy was wasteful — SHH's contract is 'IsBypassed first; if false, call GetProxy', so returning false unconditionally results in exactly one JNI round-trip per origin. * Return hostnames as UTF-16, not modified UTF-8. Promotes the existing pal_sslstream.c::AllocateString helper to a shared pal_jni helper (pal_eckey.c and pal_x509chain.c also follow this UTF-16 pattern). Managed side reads via Marshal.PtrToStringUni which is a zero-conversion copy because System.String is internally UTF-16; modified UTF-8 from GetStringUTFRegion would have required an Encoding.UTF8.GetString allocation and was not standard UTF-8 anyway. * Document the SOCKS5 transport-level proxy choice inline. * Document why UseAndroidSystemProxy is a feature switch (back-compat for apps previously on SocketsHttpHandler, trimming, testing). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
|
Tagging subscribers to this area: @karelz, @dotnet/ncl |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds Android-specific system proxy support for SocketsHttpHandler by introducing a managed AndroidPlatformProxy : IWebProxy backed by a new native PAL entrypoint that queries java.net.ProxySelector via JNI, and wires it into SystemProxyInfo with a feature switch.
Changes:
- Add native JNI/PAL support (
AndroidCryptoNative_GetProxyForUrl/...FreeProxyResult) to queryProxySelector.select(URI), returning HTTP/SOCKS proxy entries. - Add managed
AndroidPlatformProxyand Android-specificSystemProxyInfoimplementation to prefer env-var proxy first, then Android platform proxy (feature-switchable). - Update build glue (CMake +
System.Net.Http.csproj) and add Android interop declarations for the new PAL exports.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c | Removes the file-local AllocateString helper in favor of a shared PAL helper. |
| src/native/libs/System.Security.Cryptography.Native.Android/pal_proxy.h | Defines native proxy result structs/enums and PAL export signatures. |
| src/native/libs/System.Security.Cryptography.Native.Android/pal_proxy.c | Implements ProxySelector-based proxy resolution and result marshalling. |
| src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h | Adds cached JNI class/method/field IDs for proxy-related Java types and exposes AllocateString. |
| src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c | Defines/initializes the new JNI caches and provides the shared AllocateString implementation. |
| src/native/libs/System.Security.Cryptography.Native.Android/CMakeLists.txt | Adds pal_proxy.c to the native build sources. |
| src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.Android.cs | Android-specific proxy construction order + System.Net.Http.UseAndroidSystemProxy feature switch. |
| src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AndroidPlatformProxy.Android.cs | Managed IWebProxy implementation calling into the native PAL and mapping to http/socks5 URIs. |
| src/libraries/System.Net.Http/src/System.Net.Http.csproj | Includes Android-only proxy files + Android interop sources; excludes Android from the Unix fallback item group. |
| src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Proxy.cs | Adds [LibraryImport] declarations and the blittable native result struct. |
- Remove duplicate java.util.List JNI cache (g_List/g_ListSize/g_ListGet); reuse existing g_ListClass/g_ListGet and g_CollectionSize. - pal_proxy: free result and report outProxies = NULL when no proxy entries survive filtering (DIRECT-only / unknown types), matching the managed contract that count == 0 implies a null buffer. - AndroidPlatformProxy.GetProxy: always free the native result via try/finally whenever proxies != null (defense in depth against the count == 0 case). - SystemProxyInfo.Android: cache UseAndroidSystemProxy in a static get-only property initializer so the runtime switch is read once and can participate in trimming / JIT constant propagation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Return Java Proxy.Type.DIRECT entries from the Android proxy PAL and handle them explicitly in AndroidPlatformProxy so ProxySelector ordering is preserved. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move Android-specific proxy tests to an Android-only test file and document/directly handle Java Proxy.Type.DIRECT semantics while avoiding JNI exception logging during proxy resolution. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced May 27, 2026
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.
Note
This pull request (description and code) was drafted with the assistance of GitHub Copilot CLI.
Summary
Adds an
AndroidPlatformProxy : IWebProxythat resolves the system proxy chain forSocketsHttpHandleron Android by JNI-callingjava.net.ProxySelector.getDefault().select(URI.create(url)).This brings the Android system proxy — Wi-Fi proxy, MDM-deployed proxy, PAC scripts, per-network and VPN-attached
ProxyInfo— to the managed HTTP stack, matching the behaviorHttpURLConnection/AndroidMessageHandlerexhibit today. Closes the largest behavioral regression apps would face when flipping the defaultHttpClientHandlerbackend fromAndroidMessageHandlertoSocketsHttpHandleronnet*-android*(SystemProxyInfo.Unix.cscurrently honors only env vars on Android).Implementation lives entirely in
dotnet/runtime— no dependency ondotnet/android, no[UnsafeAccessor]intoMono.Android.Background
HttpClient.DefaultProxyon Android currently resolves toHttpEnvironmentProxy(env-var only) via the shared Unix path:In contrast,
java.net.HttpURLConnection(and thereforeAndroidMessageHandler) defers tojava.net.ProxySelector.getDefault(), which on Android consults system properties,ConnectivityManager.getDefaultProxy(), per-networkLinkProperties.getHttpProxy(), and PAC scripts (via the privilegedPacProxyService). All of that is silently lost when an app switches toSocketsHttpHandlertoday.What this PR changes
Native PAL (
System.Security.Cryptography.Native.Android)pal_proxy.{h,c}—AndroidCryptoNative_GetProxyForUrl/AndroidCryptoNative_FreeProxyResult..javahelper (we're just calling APIs, not implementing a Java interface).pal_jni.hmacros (INIT_LOCALS,RELEASE_LOCALS,ON_EXCEPTION_PRINT_AND_GOTO) for leak-safe local-ref handling on every exception path.INIT_LOCALS(iter, …)+RELEASE_LOCALSon everycontinueto prevent local-ref-table overflow on large proxy lists.pal_jni.{h,c}— global refs added forjava.net.ProxySelector,Proxy,Proxy$Type(HTTP/SOCKSjfieldIDs),InetSocketAddress,URI,java.util.List. All populated once inInitializeJObjectsForPal.pal_jni.{h,c}— promotesAllocateString(previously private topal_sslstream.c) into a shared PAL helper. It returns a NUL-terminated UTF-16 buffer that managed code reads viaMarshal.PtrToStringUni(zero-conversion memcpy: both Java and .NET strings are internally UTF-16).pal_sslstream.cis updated to use the shared helper.CMakeLists.txt—pal_proxy.cadded toNATIVECRYPTO_SOURCES.Managed (
System.Net.Http)AndroidPlatformProxy.Android.cs—IWebProxyimplementation (same shape asMacProxy).GetProxy(uri)P/Invokes the PAL, takes the first non-DIRECTentry, falls back to direct on any failure.IsBypassed(_) => false(matchesHttpWindowsProxy.cs:349–360rationale: computing the real answer costs the same asGetProxy, so we let SHH callGetProxyexactly once per pool key).Credentialsexists (required byIWebProxy) but is never populated by us — Android's proxy APIs do not surface credentials. Users authenticate viaHttpClientHandler.DefaultProxyCredentials, the same as on macOS (MacProxyhas the same limitation tracked as Use system proxy configuration on macOS #24799).SystemProxyInfo.Android.cs— env vars first (HttpEnvironmentProxy), thenAndroidPlatformProxy, thenHttpNoProxy. Gated by a newFeatureSwitchDefinition("System.Net.Http.UseAndroidSystemProxy")with defaulttrue.Interop.Proxy.cs—[LibraryImport]declarations.System.Net.Http.csproj— new<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'android'">adds the three managed files and the AndroidInterop.Libraries.cs. The existing fall-through ItemGroup is updated to exclude Android.Feature switch:
System.Net.Http.UseAndroidSystemProxyDefaults to
true. Apps can opt out via<RuntimeHostConfigurationOption>(or by setting the property name inruntimeconfig.json):Three legitimate reasons to disable:
SocketsHttpHandlerand got env-var-only behavior — honoring the system proxy could change which server their HTTP requests reach.AndroidPlatformProxyand the P/Invokes when set tofalseat publish time.SOCKS support
ProxySelectormay returnProxy.Type.SOCKS(transport-level proxy protocol per RFC 1928; tunnels arbitrary TCP at the socket layer). On modern AndroidProxy.Type.SOCKSmaps to SOCKS5. We translate to asocks5://host:portURI;SocketsHttpHandleraccepts that scheme viaHttpUtilities.IsSupportedProxyScheme.Verification
./build.sh libs.native -os android -arch arm64— 0 warnings, 0 errors.dotnet build src/libraries/System.Net.Http/src/System.Net.Http.csproj /p:TargetOS=android /p:TargetArchitecture=arm64— 0 warnings, 0 errors across all RIDs (also confirmedpal_sslstream.cstill links cleanly after movingAllocateStringout).nm -D libSystem.Security.Cryptography.Native.Android.soconfirms bothAndroidCryptoNative_GetProxyForUrlandAndroidCryptoNative_FreeProxyResultare exported.What this PR does NOT do (follow-ups)
AndroidPlatformTrustTestsfrom [Android] Respect platform trust manager in SslStream #124173 is the right follow-up. The test matrix should cover: Wi-Fi system proxy honored,Settings.Global.HTTP_PROXYhonored, PAC script evaluated, no-proxy fall-through, bypass list,UseProxy=falseskips, explicit handlerProxyoverrides,DefaultProxyCredentialsflows toProxy-Authorization, feature switch off.System.Security.Cryptography.Native.Androideven though the lib's scope is now broader than crypto. Renaming it (e.g.System.Net.Native.Android) is a separate, mechanical refactor.pal_eckey.candpal_x509chain.cstill have inline duplicates of theAllocateStringpattern. Hoisting them to use the shared helper is a separate cleanup.$(UseAndroidSystemProxy)property. The runtime config switch can be set explicitly today via<RuntimeHostConfigurationOption>; SDK-level wiring can come indotnet/android.Microsoft Reviewers: Open in CodeFlow