[PROTOTYPE] Remove Android time-zone change monitoring - #12247
Draft
jonathanpeppers wants to merge 3 commits into
Draft
[PROTOTYPE] Remove Android time-zone change monitoring#12247jonathanpeppers wants to merge 3 commits into
jonathanpeppers wants to merge 3 commits into
Conversation
.NET for Android is the only .NET platform that watches for system time-zone changes and invalidates managed caches. dotnet/runtime has twice declined to do this (dotnet/runtime#31043, dotnet/runtime#97010), on the grounds that listening for the OS event should not be a cost imposed on every app. Verified on an emulator (API 36): * MonoVM/net10.0-android: the existing mechanism works, updating `TimeZoneInfo.Local` live. (`CultureInfo.ClearCachedData()` transitively calls `TimeZoneInfo.ClearCachedData()`.) * CoreCLR/net11.0-android: never updates -- `host-jni.cc` is `// TODO: implement or remove`. NativeAOT shares that host. * MonoVM is not buildable at all on `net11.0-android` (`NETSDK1242`), so the feature only functions in a configuration that can no longer be targeted. So this deletes the whole stack: the `NotifyTimeZoneChanges` Java receiver and its registration in both `MonoPackageManager.java` variants, the `notifyTimeZoneChanged` JNI entry point (Java declaration, generated header, both `.map.txt` export lists, the MonoVM `timezones.cc` implementation and the CoreCLR stub), the managed `AndroidEnvironment.NotifyTimeZoneChanged()` and its ILLink preserve entry. `PreserveTest` is retargeted at `Java.Lang.Object.SetHandleOnDeserialized`, which is still in the preserve list. Apps that need this behaviour can do it themselves in a few lines:: [BroadcastReceiver (Enabled = true, Exported = false)] [IntentFilter (new [] { Intent.ActionTimezoneChanged })] public class TimeZoneChangedReceiver : BroadcastReceiver { public override void OnReceive (Context? context, Intent? intent) => TimeZoneInfo.ClearCachedData (); } `ACTION_TIMEZONE_CHANGED` is on Android's implicit-broadcast exception list, so manifest declaration is allowed; it is also a protected system broadcast, so `RegisterReceiver()` needs no API-34 export flag. Both forms were verified on device. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e170150-3891-4cdc-afa9-b8f9d62f1cfd
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e170150-3891-4cdc-afa9-b8f9d62f1cfd
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e170150-3891-4cdc-afa9-b8f9d62f1cfd
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.
Draft — alternative to #12223. Do not merge without a decision on which approach we want.
#12223 implements the CoreCLR side of the
// TODO: implement or removeinhost-jni.cc. This PR takes the other branch of that TODO: remove the feature entirely.Why
No other .NET platform does this. dotnet/runtime closed the equivalent request twice as by-design — #97010 (by-design, 2024-01-15) and #31043 (not planned, 2024-10-22). tarekgh, area owner:
The feature is MonoVM-only, and MonoVM is gone.
src/native/clr/host/host-jni.ccwas literally// TODO: implement or remove, and NativeAOT shares that host — so CoreCLR and NativeAOT have never had this. And on .NET 11+:So today this machinery only functions on a configuration customers can no longer target.
Cost of keeping it: a
registerReceiverbinder call on every app's startup path, an extra class in every APK, an ILLink preserve entry, and a foot-gun —timezones.cccalledHelpers::abort_application ()if the managed method lookup failed, so a trimming regression would crash the app rather than just leave the time zone stale.Empirical verification
Four runs on an API 36 emulator, with both apps in the same logcat as a control. Note that
adb shell setprop persist.sys.timezonedoes not fire the broadcast — you needadb shell cmd alarm set-timezone "<zone>"aftersettings put global auto_time_zone 0. (This is an easy way to get a false "fix confirmed" in manual testing.)RegisterReceiver[BroadcastReceiver]Two things worth recording, because both contradict assumptions I started with:
AndroidEnvironment.NotifyTimeZoneChanged ()only callsThread.CurrentCulture.ClearCachedData (), which looks like it wouldn't touchTimeZoneInfo. It does —CultureInfo.ClearCachedData ()in dotnet/runtimemaincallsTimeZoneInfo.ClearCachedData (),TimeZone.ResetTimeZone ()and resetsRegionInfo.s_currentRegionInfo. All static, so the calling thread is irrelevant. The mechanism is not broken; it is just redundant and unsupported-by-default.ACTION_TIMEZONE_CHANGEDis manifest-declarable (it's on Android's implicit-broadcast exception list), and being a protected system broadcast it needs noRECEIVER_EXPORTEDflag on API 34+.What an app does instead
Manifest-declared:
Or registered at runtime:
Both were confirmed to restore correct
TimeZoneInfo.Localon a CoreCLR/net11 app on API 36.Changes
NotifyTimeZoneChanges.java,timezones.cc,PreserveTest.csRuntime.javanative decl;registerReceiverin bothMonoPackageManager.javavariantshost-jni.cc/.hhstub, both.map.txtexports,CMakeLists.txt,mono_android_Runtime.hAndroidEnvironment.NotifyTimeZoneChanged (), ILLink preserve entryMainActivityReplacement.cs,InstallAndRunTests.csdotnet-local.cmd build Xamarin.Android.sln→ 0 errors. VerifiedNotifyTimeZoneChangesis absent from all fivejava_runtime*.jars andJava_mono_android_Runtime_notifyTimeZoneChangedis gone from the built.soexport tables (llvm-nm -D). Regeneratedmono_android_Runtime.hviajavac -hmatches the hand edit. An app built with the locally-built SDK installs and runs cleanly on device.Open questions before this could merge
mono.android.Runtime.notifyTimeZoneChangedispublic static nativeand the.map.txtfiles are export lists. Someone should confirm no partner or binding library calls it.NotifyTimeZoneChangedcallTimeZoneInfo.ClearCachedData ()directly rather than relying on theCultureInfotransitive path) > keep as-is.