[Microsoft.Android.Sdk.ILLink] fix crash when TZ changes (#7956)

Fixes: https://github.com/xamarin/xamarin-android/issues/7953

Context: 11f0e1bf64

When a timezone changes in a `Release` config app, it can crash with:

	[monodroid] Unable to find Android.Runtime.AndroidEnvironment.NotifyTimeZoneChanged()!

In commit 11f0e1b, we removed the line:

	<?xml version="1.0" encoding="utf-8" ?>
	<linker>
	    <assembly fullname="Mono.Android">
	--      <type fullname="Android.Runtime.AndroidEnvironment" />

Unfortunately, `AndroidEnvironment.NotifyTimeZoneChanged()` is called
from non-managed code, via:

  * The `NotifyTimeZoneChanges` broadcast receiver
    (in `src/java-runtime/java/mono/android/app/NotifyTimeZoneChanges.java`)
    which calls-
  * The `Rutime.notifyTimeZoneChanged()` `native` method
    (in `src/java-runtime/java/mono/android/Runtime.java`),
    implemented in-
  * `Java_mono_android_Runtime_notifyTimeZoneChanged()`
    (in`src/monodroid/jni/timezones.cc`).

The managed linker cannot "see" any of these references, so we
need to *always* preserve this method, as it is always callable.

Update `src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml`
so that `Android.Runtime.AndroidEnvironment.NotifyTimeZoneChanged()`
is always preserved.

Added a test for this scenario.

TODO: we may want to audit all `mono_class_get_method_from_name()`
calls and add more tests cases.

I also cleaned up the tests a bit with a `getResource()` local function.
This commit is contained in:
Jonathan Peppers 2023-04-13 12:43:21 -05:00 коммит произвёл GitHub
Родитель 82161e07a2
Коммит 59e1e466bc
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 42 добавлений и 20 удалений

Просмотреть файл

@ -2,6 +2,9 @@
<linker>
<assembly fullname="Mono.Android">
<type fullname="Android.Runtime.AnnotationAttribute" />
<type fullname="Android.Runtime.AndroidEnvironment">
<method name="NotifyTimeZoneChanged" />
</type>
<type fullname="Android.Runtime.IJavaObject" />
<type fullname="Android.Runtime.InputStreamAdapter" preserve="methods" />
<type fullname="Android.Runtime.InputStreamInvoker" preserve="methods" />

Просмотреть файл

@ -115,6 +115,9 @@ namespace UnnamedProject
// [Test] Post
Android.Util.Log.Info(TAG, HttpClientTest.Post ());
// [Test] MethodsArePreserved
Android.Util.Log.Info (TAG, PreserveTest.MethodsArePreserved ());
// [Test] TextChanged
Android.Util.Log.Info (TAG, MaterialTextChanged.TextChanged (this));

Просмотреть файл

@ -0,0 +1,22 @@
using System;
using System.Reflection;
public class PreserveTest
{
// [Test]
public static string MethodsArePreserved ()
{
try {
// See src/monodroid/jni/timezones.cc for usage
var androidEnvironment = Type.GetType ("Android.Runtime.AndroidEnvironment, Mono.Android", throwOnError: true);
var notifyTimeZoneChanged = androidEnvironment.GetMethod ("NotifyTimeZoneChanged", BindingFlags.Static | BindingFlags.NonPublic);
if (notifyTimeZoneChanged == null) {
return $"[FAIL] {nameof (PreserveTest)}.{nameof (MethodsArePreserved)} FAILED: {nameof (notifyTimeZoneChanged)} is null)";
}
notifyTimeZoneChanged.Invoke (null, null);
return $"[PASS] {nameof (PreserveTest)}.{nameof (MethodsArePreserved)}";
} catch (Exception ex) {
return $"[FAIL] {nameof (PreserveTest)}.{nameof (MethodsArePreserved)} FAILED: {ex}";
}
}
}

Просмотреть файл

@ -400,22 +400,16 @@ namespace Library1 {
},
Sources = {
new BuildItem.Source ("Bug21578.cs") {
TextContent = () => {
using (var sr = new StreamReader (typeof (InstallAndRunTests).Assembly.GetManifestResourceStream ("Xamarin.Android.Build.Tests.Resources.LinkDescTest.Bug21578.cs")))
return sr.ReadToEnd ();
},
TextContent = () => getResource("Bug21578")
},
new BuildItem.Source ("Bug35195.cs") {
TextContent = () => {
using (var sr = new StreamReader (typeof (InstallAndRunTests).Assembly.GetManifestResourceStream ("Xamarin.Android.Build.Tests.Resources.LinkDescTest.Bug35195.cs")))
return sr.ReadToEnd ();
},
TextContent = () => getResource("Bug35195")
},
new BuildItem.Source ("HttpClientTest.cs") {
TextContent = () => {
using (var sr = new StreamReader (typeof (InstallAndRunTests).Assembly.GetManifestResourceStream ("Xamarin.Android.Build.Tests.Resources.LinkDescTest.HttpClientTest.cs")))
return sr.ReadToEnd ();
},
TextContent = () => getResource("HttpClientTest")
},
new BuildItem.Source ("PreserveTest.cs") {
TextContent = () => getResource("PreserveTest")
},
},
};
@ -424,10 +418,7 @@ namespace Library1 {
// DataContractSerializer is not trimming safe
// https://github.com/dotnet/runtime/issues/45559
lib2.Sources.Add (new BuildItem.Source ("Bug36250.cs") {
TextContent = () => {
using (var sr = new StreamReader (typeof (InstallAndRunTests).Assembly.GetManifestResourceStream ("Xamarin.Android.Build.Tests.Resources.LinkDescTest.Bug36250.cs")))
return sr.ReadToEnd ();
},
TextContent = () => getResource ("Bug36250")
});
}
@ -450,10 +441,7 @@ namespace Library1 {
},
Sources = {
new BuildItem.Source ("MaterialTextChanged.cs") {
TextContent = () => {
using (var sr = new StreamReader (typeof (InstallAndRunTests).Assembly.GetManifestResourceStream ("Xamarin.Android.Build.Tests.Resources.LinkDescTest.MaterialTextChanged.cs")))
return sr.ReadToEnd ();
},
TextContent = () => getResource ("MaterialTextChanged")
},
},
OtherBuildItems = {
@ -512,6 +500,12 @@ namespace Library1 {
StringAssert.Contains ("[LINKALLPASS]", logcatOutput);
StringAssert.DoesNotContain ("[LINKALLFAIL]", logcatOutput);
}
string getResource (string name)
{
using (var sr = new StreamReader (typeof (InstallAndRunTests).Assembly.GetManifestResourceStream ($"Xamarin.Android.Build.Tests.Resources.LinkDescTest.{name}.cs")))
return sr.ReadToEnd ();
}
}
[Test]